rails-pdf-renderer 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/.claude/skills/commit/SKILL.md +76 -0
- data/.claude/skills/create-pr/SKILL.md +170 -0
- data/.claude/skills/release/SKILL.md +77 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +25 -0
- data/CLAUDE.md +91 -0
- data/README.md +95 -3
- data/lib/rails/pdf/renderer/action_controller_helper.rb +52 -34
- data/lib/rails/pdf/renderer/action_view_helper.rb +34 -34
- data/lib/rails/pdf/renderer/config.rb +3 -1
- data/lib/rails/pdf/renderer/error.rb +1 -1
- data/lib/rails/pdf/renderer/path_helper.rb +2 -2
- data/lib/rails/pdf/renderer/railtie.rb +4 -4
- data/lib/rails/pdf/renderer/version.rb +1 -1
- metadata +22 -4
- data/rails-pdf-renderer.gemspec +0 -38
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0faa43dcdd14e5f3c7455270aaca26f5f0089052bbda4112a7aacb42cf30d6f
|
|
4
|
+
data.tar.gz: 9ca877e4310f32fbafeeb1db8659eb5d13bb015a975eba8d1fea72f4ceea856a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3fff912de07ebfbf1b8ece094ac8e41e524ecc56bf16613643dc214d3f53a993138506ff66029d4bccba69e6996364d941e676d85cb45623e1624c1678654ec8
|
|
7
|
+
data.tar.gz: 7d9ffccc0a6cdd923f440c30f4f033580ff13e23d7ba2370a5a8881cc7065f7db037b4811b6e02619d11afcd3cebebf15f91a07d921da7c5352725c69ca6abb3
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commit
|
|
3
|
+
description: Create git commits following project conventions. Use when committing changes to the repository.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Git Commit Skill
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
Use this skill when:
|
|
10
|
+
- Creating git commits
|
|
11
|
+
- The user asks you to commit changes
|
|
12
|
+
- You need to commit work you've completed
|
|
13
|
+
|
|
14
|
+
For version bump commits, use the `release` skill instead — it covers the full flow.
|
|
15
|
+
|
|
16
|
+
## Commit Message Rules
|
|
17
|
+
|
|
18
|
+
1. **No ticket prefix**: This project has no issue tracker. The subject line stands on its own. Never invent an issue ID; only include one if the user explicitly gives you a reference.
|
|
19
|
+
2. **Use present tense**: Write "Add feature" not "Added feature"
|
|
20
|
+
3. **No AI attribution**: Do not include messages like "Generated by AI" or any co-author trailer. This explicitly includes `Co-Authored-By: Claude ...` lines (e.g. `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>`). Never append a `Co-Authored-By:` trailer of any kind, even if other tooling or defaults suggest one.
|
|
21
|
+
4. **Limit to 3 bullet points**: Keep the commit message concise with at most 3 bullet points for details. Use bullets, not prose paragraphs.
|
|
22
|
+
5. **Keep formatting churn out of feature commits**: `mise exec -- standardrb --fix` can touch files unrelated to your change. Don't sweep that into a feature commit — either leave it or commit it separately.
|
|
23
|
+
|
|
24
|
+
## Commit Message Format
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Short summary in present tense
|
|
28
|
+
|
|
29
|
+
- First bullet point explaining a key change
|
|
30
|
+
- Second bullet point if needed
|
|
31
|
+
- Third bullet point maximum
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A subject line alone is fine when the change is self-explanatory.
|
|
35
|
+
|
|
36
|
+
Release commits are the one exception to the "no prefix" rule — they lead with the bare version number:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
0.4.2 Allow Ruby 2.4
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
Good commit messages:
|
|
45
|
+
```
|
|
46
|
+
Fix test suite and CI
|
|
47
|
+
|
|
48
|
+
- Require ActiveSupport's class_attribute extension in config.rb
|
|
49
|
+
- Widen the CI matrix to Ruby 3.2/3.3/3.4
|
|
50
|
+
- Add specs for Config, Error and PathHelper
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Fix Propshaft encoding issue
|
|
55
|
+
|
|
56
|
+
- Force UTF-8 on assets resolved through Propshaft
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Fix config errors
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Bad commit messages:
|
|
64
|
+
```
|
|
65
|
+
Added new feature for users
|
|
66
|
+
|
|
67
|
+
- This was generated by Claude
|
|
68
|
+
- Co-Authored-By: Claude <noreply@anthropic.com>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Workflow
|
|
72
|
+
|
|
73
|
+
1. Run `mise exec -- bundle exec rake` (rspec + standardrb) — this is exactly what CI runs on every push and PR. Fix anything it reports before committing. Always prefix Ruby commands with `mise exec --` so they use the Ruby pinned in `.tool-versions` instead of the system Ruby.
|
|
74
|
+
2. Stage the relevant changes with `git add`
|
|
75
|
+
3. Create a commit with a message following the rules above
|
|
76
|
+
4. Verify the commit was created successfully with `git log -1`
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-pr
|
|
3
|
+
description: Create a GitHub pull request with a well-structured description. Use when creating a PR or when the user asks to open a pull request.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create PR Skill
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
Use this skill when:
|
|
10
|
+
- Creating a GitHub pull request
|
|
11
|
+
- The user asks you to open or create a PR
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
1. **Gather context**: Run `git log main..HEAD --oneline` and `git diff main...HEAD --stat` to understand all changes
|
|
16
|
+
2. **Read changed files** if needed to understand the intent behind the changes
|
|
17
|
+
3. **Write the PR description** following the format below
|
|
18
|
+
4. **Create the PR** using `gh pr create --base main`
|
|
19
|
+
|
|
20
|
+
PRs target `main` unless the user says otherwise (stacked PRs onto another feature branch happen, but are the exception). Branches are lowercase kebab-case with no namespace prefix — `fix-tests-and-ci`, `allow-ruby-2-4`.
|
|
21
|
+
|
|
22
|
+
## PR Title Format
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Short summary in present tense
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No ticket prefix and no conventional-commit prefix. In practice the title matches the branch's commit subject. Keep it under 70 characters.
|
|
29
|
+
|
|
30
|
+
## PR Description Format
|
|
31
|
+
|
|
32
|
+
```markdown
|
|
33
|
+
## Summary
|
|
34
|
+
|
|
35
|
+
Brief description of what this PR does and why. Focus on the intent and approach,
|
|
36
|
+
not a mechanical list of file changes. 2-5 sentences.
|
|
37
|
+
|
|
38
|
+
## Verification
|
|
39
|
+
|
|
40
|
+
- [ ] `mise exec -- bundle exec rake` passes (rspec + standardrb)
|
|
41
|
+
- [ ] Any extra checks specific to this change
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`mise exec -- bundle exec rake` is the project's gate — CI runs it on Ruby 3.3, 3.4 and 4.0. Run Ruby commands through `mise exec --` so they use the Ruby pinned in `.tool-versions` rather than the system Ruby. If you ran it against more than one Ruby version, or deleted `Gemfile.lock` to simulate a clean checkout, say so.
|
|
45
|
+
|
|
46
|
+
Only claim a check you actually ran.
|
|
47
|
+
|
|
48
|
+
## Bug fix PRs
|
|
49
|
+
|
|
50
|
+
For bug fixes, frame the description around behavior, not code. A reviewer wants to know
|
|
51
|
+
what triggered the bug, what we expected, and what actually happened — not what was wrong with
|
|
52
|
+
the old code. Use this format:
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
## Summary
|
|
56
|
+
|
|
57
|
+
One or two sentences describing the problem and the fix.
|
|
58
|
+
|
|
59
|
+
**Steps to reproduce**
|
|
60
|
+
1. ...
|
|
61
|
+
2. ...
|
|
62
|
+
|
|
63
|
+
**Expected:** what should happen
|
|
64
|
+
**Actual:** what happened instead (the bug)
|
|
65
|
+
|
|
66
|
+
## Verification
|
|
67
|
+
|
|
68
|
+
- [ ] ...
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Rules for the reproduction steps:
|
|
72
|
+
|
|
73
|
+
- **Minimal**: include only the steps needed to trigger the bug. Eliminate as many
|
|
74
|
+
factors as possible so the cause is isolated.
|
|
75
|
+
- **Reproducible from a clean checkout**: this is a gem, so the repro is code-level — a
|
|
76
|
+
short snippet, a specific config, a Ruby or Rails version, an asset pipeline setup.
|
|
77
|
+
Name the versions involved when they matter.
|
|
78
|
+
- **Start from a known state**: e.g. "in a fresh Rails 8.1 app with Propshaft" rather
|
|
79
|
+
than assuming the reader's local setup.
|
|
80
|
+
|
|
81
|
+
Keep the `## Verification` section in addition — it confirms the fix works, while the
|
|
82
|
+
reproduction steps document the bug.
|
|
83
|
+
|
|
84
|
+
## Rules
|
|
85
|
+
|
|
86
|
+
1. **No AI attribution**: Do not include "Generated by Claude Code", co-author lines, or similar
|
|
87
|
+
2. **Summary should explain why, not just what**: Describe the problem being solved and the approach taken
|
|
88
|
+
3. **Describe behavior, not code internals**: For bug fixes, give what triggered the bug, what we expected, and what actually happened. Do not explain in detail what was wrong with the old code or what it was before — that's irrelevant to a reviewer
|
|
89
|
+
4. **Verification is what you actually ran**: Name the commands and their results. Don't list checks you skipped
|
|
90
|
+
5. **Skip the verification section** if there is nothing meaningful to report (e.g. doc-only changes)
|
|
91
|
+
6. **Use present tense**: Write "Add feature" not "Added feature"
|
|
92
|
+
7. **Keep it concise**: The description should be helpful, not exhaustive
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
Good PR description:
|
|
97
|
+
```markdown
|
|
98
|
+
## Summary
|
|
99
|
+
|
|
100
|
+
The suite never got past loading, both locally and on CI: `config.rb` calls `class_attribute`
|
|
101
|
+
but nothing required ActiveSupport's core extension, so requiring `rails/pdf/renderer`
|
|
102
|
+
standalone (as `spec_helper` does) raised `NoMethodError`. Adding that one require is the
|
|
103
|
+
actual fix. The default `rake` task also chains `standard`, which was reporting 75 offenses
|
|
104
|
+
and aborting the task, so this applies `standard:fix` across the lib and gemspec. CI moves to
|
|
105
|
+
`actions/checkout@v4` and widens the matrix from a single `3.2.2` pin to Ruby 3.2/3.3/3.4.
|
|
106
|
+
|
|
107
|
+
## Verification
|
|
108
|
+
|
|
109
|
+
- [ ] `mise exec -- bundle exec rake` passes on Ruby 4.0.6 — 19 examples, 0 failures
|
|
110
|
+
- [ ] Passes from scratch with `Gemfile.lock` deleted, simulating a clean CI checkout
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Good bug-fix PR description (reproduce / expected / actual):
|
|
114
|
+
```markdown
|
|
115
|
+
## Summary
|
|
116
|
+
|
|
117
|
+
Rendering a PDF in a Rails app on Propshaft raised an encoding error when a stylesheet
|
|
118
|
+
contained non-ASCII characters. Assets resolved through Propshaft are now forced to UTF-8.
|
|
119
|
+
|
|
120
|
+
**Steps to reproduce**
|
|
121
|
+
1. In a Rails 7.1 app using Propshaft, add a stylesheet containing a non-ASCII character
|
|
122
|
+
2. Render any PDF that includes that stylesheet
|
|
123
|
+
|
|
124
|
+
**Expected:** the PDF renders with the stylesheet applied
|
|
125
|
+
**Actual:** `Encoding::CompatibilityError` is raised during rendering
|
|
126
|
+
|
|
127
|
+
## Verification
|
|
128
|
+
|
|
129
|
+
- [ ] `mise exec -- bundle exec rake` passes
|
|
130
|
+
- [ ] Rendering a PDF with a non-ASCII stylesheet succeeds in a Propshaft app
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Good PR description (no verification section needed):
|
|
134
|
+
```markdown
|
|
135
|
+
## Summary
|
|
136
|
+
|
|
137
|
+
Point the gemspec's homepage, source_code_uri and changelog_uri at `gembero/rails-pdf-renderer`.
|
|
138
|
+
They were pointing at a bare `github.com/erikaxel/` URL from before the repo moved to the org.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Bad PR description:
|
|
142
|
+
```markdown
|
|
143
|
+
## Summary
|
|
144
|
+
|
|
145
|
+
Updated files to fix the bug.
|
|
146
|
+
|
|
147
|
+
## Verification
|
|
148
|
+
|
|
149
|
+
- [ ] Run the tests
|
|
150
|
+
- [ ] Check CI passes
|
|
151
|
+
|
|
152
|
+
🤖 Generated with Claude Code
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Creating the PR
|
|
156
|
+
|
|
157
|
+
Use `gh pr create` with a HEREDOC for the body:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
gh pr create --base main --title "Title here" --body "$(cat <<'EOF'
|
|
161
|
+
## Summary
|
|
162
|
+
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
## Verification
|
|
166
|
+
|
|
167
|
+
- [ ] ...
|
|
168
|
+
EOF
|
|
169
|
+
)"
|
|
170
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release
|
|
3
|
+
description: Cut a new version of the rails-pdf-renderer gem — bump the version, update the changelog, and publish to rubygems.org. Use when releasing a new version or bumping the gem version.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release Skill
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
Use this skill when:
|
|
10
|
+
- The user asks to cut, ship, or publish a release
|
|
11
|
+
- The user asks to bump the gem version
|
|
12
|
+
|
|
13
|
+
## Before you start
|
|
14
|
+
|
|
15
|
+
Check that the last release actually shipped:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git tag # tags are v-prefixed: v0.4.1
|
|
19
|
+
cat lib/rails/pdf/renderer/version.rb # VERSION = "0.4.2"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If `version.rb` is ahead of the newest tag, a previous version was committed but never
|
|
23
|
+
released. Ask the user whether to release that version as-is or to bump past it — don't
|
|
24
|
+
silently skip it.
|
|
25
|
+
|
|
26
|
+
Also confirm you're releasing from `main` and that it's up to date with `origin/main`.
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
1. **Bump the version** in `lib/rails/pdf/renderer/version.rb`. The gemspec reads
|
|
31
|
+
`RailsPdfRenderer::VERSION` from this file — it's the single source of truth. Follow
|
|
32
|
+
semver: patch for fixes, minor for backwards-compatible features, major for breaking
|
|
33
|
+
changes.
|
|
34
|
+
|
|
35
|
+
2. **Add a `CHANGELOG.md` entry.** This step is not optional — the changelog was skipped
|
|
36
|
+
for the entire 0.4.x line and is stale as a result. Match the existing format:
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
## [0.5.0]
|
|
40
|
+
### New Features
|
|
41
|
+
- Description of the feature
|
|
42
|
+
|
|
43
|
+
### Fixes
|
|
44
|
+
- Description of the fix
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Newest version goes at the top, directly under the header block. Use `### New Features`
|
|
48
|
+
and `### Fixes` sections with dash bullets; include only the sections that apply.
|
|
49
|
+
|
|
50
|
+
3. **Run the gate**: `mise exec -- bundle exec rake` (rspec + standardrb). It must be green
|
|
51
|
+
before you release. CI runs this on Ruby 3.3, 3.4 and 4.0.
|
|
52
|
+
|
|
53
|
+
4. **Commit** the version bump and changelog together, with the version as the subject
|
|
54
|
+
prefix (see the `commit` skill for the general rules — no AI attribution, present tense):
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
0.5.0 Add support for custom page sizes
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
A bare version subject (`0.4.0`) is acceptable when the release has no single headline
|
|
61
|
+
change, but a short description is better.
|
|
62
|
+
|
|
63
|
+
5. **Publish**: `mise exec -- bundle exec rake release`. This comes from `bundler/gem_tasks` and will:
|
|
64
|
+
- create the `v0.5.0` git tag
|
|
65
|
+
- push the commit and tag to `origin`
|
|
66
|
+
- build the gem and push it to rubygems.org
|
|
67
|
+
|
|
68
|
+
It needs rubygems credentials and it is not reversible — a published version cannot be
|
|
69
|
+
replaced, only yanked. Confirm with the user before running it.
|
|
70
|
+
|
|
71
|
+
## Notes
|
|
72
|
+
|
|
73
|
+
- The local Ruby version is pinned in `.tool-versions`. Run every Ruby command through
|
|
74
|
+
`mise exec --` so it doesn't fall back to the system Ruby.
|
|
75
|
+
- `Gemfile.lock` is gitignored, so there's no lockfile to update.
|
|
76
|
+
- `mise exec -- bundle exec rake build` produces the gem locally under `pkg/` without
|
|
77
|
+
publishing — useful for a dry run.
|
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 4.0.6
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.5.0]
|
|
6
|
+
### New Features
|
|
7
|
+
- Added the `html:` option for already-rendered HTML. The string is sent to the PDF
|
|
8
|
+
service verbatim and is never compiled as a template, so `<%= %>` in the content
|
|
9
|
+
cannot execute as Ruby on the server. `layout:`, `locals:`, `assigns:`, `formats:`
|
|
10
|
+
and `handlers:` do not apply to `html:`, and passing both `html:` and `inline:`
|
|
11
|
+
raises `ArgumentError`.
|
|
12
|
+
- `type:` is now forwarded to ActionView when rendering `inline:` templates, so a
|
|
13
|
+
non-ERB handler can be used (for example `type: :raw` or `type: :haml`).
|
|
14
|
+
- Documented `inline:` (an ERB template) versus `html:` (finished HTML) in the README.
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
- Declare `activesupport` (>= 7.0) as a runtime dependency. It is required at
|
|
18
|
+
load time by `RailsPdfRenderer::Config`, but was previously only pulled in
|
|
19
|
+
through the host application.
|
|
20
|
+
- `status:` is now forwarded to the response for both PDF and `show_as_html:` renders.
|
|
21
|
+
It was previously accepted and silently ignored, so those responses always returned 200.
|
|
22
|
+
- `RailsPdfRenderer::ActionControllerHelper` now requires the standard library and
|
|
23
|
+
ActiveSupport extensions it uses instead of relying on the host application.
|
|
24
|
+
|
|
25
|
+
### Breaking changes
|
|
26
|
+
- Dropped support for end-of-life Ruby versions. `required_ruby_version` is now
|
|
27
|
+
`>= 3.3.0`. Only Ruby versions still supported by the Ruby core team are
|
|
28
|
+
supported (currently 3.3, 3.4 and 4.0).
|
|
29
|
+
|
|
5
30
|
## [0.3.0]
|
|
6
31
|
### Fixes
|
|
7
32
|
Fix Propshaft encoding issue
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code working in this repository.
|
|
4
|
+
|
|
5
|
+
## What this gem is
|
|
6
|
+
|
|
7
|
+
`rails-pdf-renderer` is a Rails gem that renders a controller view to HTML and then
|
|
8
|
+
POSTs that HTML to an **external PDF rendering service** (a headless-Chrome-style
|
|
9
|
+
server), which returns the PDF bytes. It does *not* shell out to a local binary.
|
|
10
|
+
This is the main architectural difference from [wicked_pdf](https://github.com/mileszs/wicked_pdf/),
|
|
11
|
+
from which much of the asset-handling code was adapted.
|
|
12
|
+
|
|
13
|
+
Usage in an app looks like `render pdf: "filename", template: "..."` — the gem
|
|
14
|
+
intercepts `render`/`render_to_string` when the options hash contains a `:pdf` key.
|
|
15
|
+
|
|
16
|
+
## Ruby toolchain
|
|
17
|
+
|
|
18
|
+
The local Ruby version is managed by [mise](https://mise.jdx.dev) and pinned in `.tool-versions`
|
|
19
|
+
(Ruby 4.0). The gem itself supports Ruby 3.3+; CI covers 3.3, 3.4 and 4.0.
|
|
20
|
+
|
|
21
|
+
**Always run Ruby commands through `mise exec --`** so they use the pinned Ruby instead of
|
|
22
|
+
the system Ruby. If `mise install` hasn't been run in a fresh checkout, run it first.
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
mise exec -- bin/setup # bundle install
|
|
28
|
+
mise exec -- bundle exec rake # default task: rspec + standardrb (what CI runs)
|
|
29
|
+
mise exec -- bundle exec rake spec # tests only
|
|
30
|
+
mise exec -- bundle exec rspec # tests only
|
|
31
|
+
mise exec -- standardrb # lint only; add --fix to autocorrect
|
|
32
|
+
mise exec -- bin/console # IRB with the gem loaded
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
CI (`.github/workflows/main.yml`) runs `bundle exec rake` against Ruby 3.3, 3.4 and 4.0,
|
|
36
|
+
matching the gemspec's `required_ruby_version >= 3.3.0`.
|
|
37
|
+
|
|
38
|
+
## Code layout
|
|
39
|
+
|
|
40
|
+
All code lives under `lib/rails/pdf/renderer/`, namespaced under the top-level
|
|
41
|
+
**class** `RailsPdfRenderer` (a class, not a module — `class RailsPdfRenderer` is
|
|
42
|
+
required when reopening it).
|
|
43
|
+
|
|
44
|
+
- `railtie.rb` — wiring. Prepends `ActionControllerHelper` onto `ActionController::Base`
|
|
45
|
+
and includes `ActionViewHelper` into views via `ActiveSupport.on_load`. Also
|
|
46
|
+
registers the `application/pdf` MIME type. Everything is guarded by
|
|
47
|
+
`if defined?(Rails.env)` so the gem can be required outside Rails.
|
|
48
|
+
- `action_controller_helper.rb` — the `render`/`render_to_string` overrides,
|
|
49
|
+
`make_pdf`, and `pdf_from_server` (the HTTP call to the PDF service).
|
|
50
|
+
- `action_view_helper.rb` — the bulk of the code: `pdf_asset_path`,
|
|
51
|
+
`pdf_stylesheet_link_tag`, `pdf_image_tag`, `pdf_*_base64`, etc. These inline
|
|
52
|
+
assets into the HTML so the remote renderer can resolve them. Supports Sprockets,
|
|
53
|
+
Propshaft, and Webpacker/Shakapacker; each has its own branch in `find_asset`
|
|
54
|
+
and `webpacker_source_url`. Touching one branch usually means checking the others.
|
|
55
|
+
- `config.rb` — configuration, built on `class_attribute`.
|
|
56
|
+
- `error.rb` — `RailsPdfRenderer::Error`, wraps the failed `Net::HTTP` response.
|
|
57
|
+
- `path_helper.rb`, `version.rb`.
|
|
58
|
+
|
|
59
|
+
## Things that will bite you
|
|
60
|
+
|
|
61
|
+
- **Config is global class state.** `Config` uses `class_attribute`, and
|
|
62
|
+
`RailsPdfRenderer.configuration` memoizes a single instance, so assigning a
|
|
63
|
+
value mutates the class default for the whole process. Tests that change
|
|
64
|
+
configuration must restore it.
|
|
65
|
+
- **`pdf_server_params` unconditionally reads `options[:margin][:top]`** and the
|
|
66
|
+
other margin keys. The default `margin` hash comes from `Config.default_options`,
|
|
67
|
+
which is merged in by `render`. Any code path that builds options without that
|
|
68
|
+
merge will `NoMethodError` on `nil`.
|
|
69
|
+
- **`auth_key` is base64-encoded before being sent** as `Authorization: Bearer <base64>`.
|
|
70
|
+
A missing `auth_key` raises a plain `RuntimeError`, not `RailsPdfRenderer::Error`.
|
|
71
|
+
- The controller helper's `make_pdf` calls `render_to_string` recursively — the
|
|
72
|
+
override checks for the `:pdf` key to avoid infinite recursion, so never leave
|
|
73
|
+
`:pdf` in the options hash passed downward (`make_pdf` deletes it explicitly).
|
|
74
|
+
- Assets read from disk or over HTTP are `force_encoding('UTF-8')`'d — this was a
|
|
75
|
+
deliberate fix for Propshaft (see CHANGELOG 0.3.0), don't remove it.
|
|
76
|
+
- `raise_on_missing_assets` defaults to `true`, so a missing stylesheet or image
|
|
77
|
+
raises rather than silently rendering a blank PDF.
|
|
78
|
+
|
|
79
|
+
## Style
|
|
80
|
+
|
|
81
|
+
`standard` (standardrb) is the linter and part of the default rake task, but
|
|
82
|
+
existing code in `action_controller_helper.rb` and `action_view_helper.rb` uses
|
|
83
|
+
hashrocket syntax and single quotes inherited from wicked_pdf. Match the
|
|
84
|
+
surrounding file rather than reformatting; run `standardrb` before committing and
|
|
85
|
+
keep unrelated formatting churn out of diffs.
|
|
86
|
+
|
|
87
|
+
## Releasing
|
|
88
|
+
|
|
89
|
+
1. Bump `VERSION` in `lib/rails/pdf/renderer/version.rb`.
|
|
90
|
+
2. Add an entry to `CHANGELOG.md`.
|
|
91
|
+
3. `mise exec -- bundle exec rake release` (tags, pushes, publishes to rubygems.org).
|
data/README.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# RailsPdfRenderer
|
|
2
2
|
|
|
3
|
+
## Ruby support
|
|
4
|
+
|
|
5
|
+
This gem only supports Ruby versions that are still supported by the Ruby core
|
|
6
|
+
team. When a Ruby version reaches end-of-life it is dropped from the CI matrix
|
|
7
|
+
and `required_ruby_version` is raised in the next release, which may happen in a
|
|
8
|
+
minor version bump.
|
|
9
|
+
|
|
10
|
+
The currently supported versions are Ruby 3.3, 3.4 and 4.0.
|
|
11
|
+
|
|
3
12
|
## Installation
|
|
4
13
|
|
|
5
14
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -8,17 +17,100 @@ Install the gem and add to the application's Gemfile by executing:
|
|
|
8
17
|
|
|
9
18
|
## Usage
|
|
10
19
|
|
|
20
|
+
Render the current controller action as a PDF:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
class InvoicesController < ApplicationController
|
|
24
|
+
def show
|
|
25
|
+
render pdf: "invoice-#{@invoice.number}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or get the PDF back as a string:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
pdf = ApplicationController.renderer.render_to_string(pdf: true, template: "invoices/show")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `inline:` vs `html:`
|
|
37
|
+
|
|
38
|
+
These two options look similar and are **not** interchangeable:
|
|
39
|
+
|
|
40
|
+
| Option | Content | Processed by |
|
|
41
|
+
| --------- | --------------------------------- | ------------------------------------------ |
|
|
42
|
+
| `inline:` | An **ERB template** source string | ActionView - `<%= %>` is evaluated as Ruby |
|
|
43
|
+
| `html:` | **Finished HTML** | Nothing - sent to the PDF service verbatim |
|
|
44
|
+
|
|
45
|
+
Use `html:` whenever the HTML has already been rendered - ViewComponent output, a
|
|
46
|
+
`render_to_string` from elsewhere, a stored email body, or any content influenced by user
|
|
47
|
+
input:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
html = InvoiceComponent.new(invoice: @invoice).render_in(view_context)
|
|
51
|
+
ApplicationController.renderer.render_to_string(pdf: true, html: html)
|
|
52
|
+
```
|
|
11
53
|
|
|
54
|
+
> **Security:** passing already-rendered HTML to `inline:` makes ActionView compile it as an
|
|
55
|
+
> ERB template, so any `<%= ... %>` in the content executes as Ruby on your server. Always use
|
|
56
|
+
> `html:` for content you did not author as a template.
|
|
57
|
+
|
|
58
|
+
`html:` is sent as-is, so `layout:`, `locals:`, `assigns:`, `formats:` and `handlers:` are
|
|
59
|
+
ignored, and passing both `html:` and `inline:` raises `ArgumentError`.
|
|
60
|
+
|
|
61
|
+
If you need a non-ERB *template* language for `inline:`, pass `type:` - it is forwarded to
|
|
62
|
+
ActionView's handler lookup, e.g. `type: :raw` or `type: :haml`.
|
|
63
|
+
|
|
64
|
+
### Options
|
|
65
|
+
|
|
66
|
+
| Option | Description |
|
|
67
|
+
| ------ | ----------- |
|
|
68
|
+
| `pdf:` | Filename without the `.pdf` extension, when using `render` |
|
|
69
|
+
| `html:` | Finished HTML, sent to the PDF service verbatim |
|
|
70
|
+
| `inline:` | ERB template source |
|
|
71
|
+
| `template:`, `layout:`, `locals:`, `assigns:`, `formats:`, `handlers:`, `file:`, `type:` | Standard ActionView rendering options |
|
|
72
|
+
| `show_as_html:` | Render the HTML in the browser instead of a PDF, for debugging |
|
|
73
|
+
| `status:` | HTTP status for the response |
|
|
74
|
+
| `disposition:` | `"inline"` (default) or `"attachment"` |
|
|
75
|
+
| `save_to_file:` | Also write the PDF to this path |
|
|
76
|
+
| `save_only:` | Write the PDF to `save_to_file:` without sending a response |
|
|
77
|
+
| `orientation:`, `pageSize:`, `zoom:`, `height:`, `width:`, `margin:`, `footerTemplate:` | Forwarded to the PDF service |
|
|
78
|
+
|
|
79
|
+
### Configuration
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
RailsPdfRenderer.configure do |config|
|
|
83
|
+
config.url = "https://your-pdf-service.example.com/render"
|
|
84
|
+
config.auth_key = Rails.application.credentials.pdf_service_key
|
|
85
|
+
config.default_options = {margin: {top: "10mm", bottom: "10mm", left: "0mm", right: "0mm"}}
|
|
86
|
+
end
|
|
87
|
+
```
|
|
12
88
|
|
|
13
89
|
## Development
|
|
14
90
|
|
|
15
|
-
|
|
91
|
+
The local Ruby version is managed with [mise](https://mise.jdx.dev) and pinned in
|
|
92
|
+
`.tool-versions`. Install it once with `mise install`, and run every command through mise so it
|
|
93
|
+
uses the pinned Ruby rather than the system one:
|
|
94
|
+
|
|
95
|
+
$ mise install
|
|
96
|
+
$ mise exec -- bin/setup
|
|
97
|
+
|
|
98
|
+
If you have mise activated in your shell (`mise activate`), the `mise exec --` prefix is
|
|
99
|
+
unnecessary — the pinned Ruby is already on your `PATH`. The examples below spell it out so
|
|
100
|
+
they work either way.
|
|
101
|
+
|
|
102
|
+
Run the tests with `mise exec -- rake spec`, or `mise exec -- bundle exec rake` for the full
|
|
103
|
+
gate (rspec + standardrb) that CI runs. You can also run `mise exec -- bin/console` for an
|
|
104
|
+
interactive prompt that will allow you to experiment.
|
|
105
|
+
|
|
106
|
+
To install this gem onto your local machine, run `mise exec -- bundle exec rake install`.
|
|
16
107
|
|
|
17
|
-
|
|
108
|
+
Development happens on Ruby 4.0 even though the gem supports 3.3 and up; CI is what covers
|
|
109
|
+
the rest of the matrix.
|
|
18
110
|
|
|
19
111
|
## Releasing new gems
|
|
20
112
|
1. Update version.rb
|
|
21
|
-
2. Run `rake release` to release a new gem
|
|
113
|
+
2. Run `mise exec -- bundle exec rake release` to release a new gem
|
|
22
114
|
|
|
23
115
|
## Contributing
|
|
24
116
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
require "json"
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "active_support/core_ext/object/blank"
|
|
5
|
+
require "active_support/core_ext/string/output_safety"
|
|
6
|
+
|
|
1
7
|
class RailsPdfRenderer
|
|
2
8
|
module ActionControllerHelper
|
|
3
|
-
def self.prepended(base)
|
|
4
|
-
# Protect from trying to augment modules that appear
|
|
5
|
-
# as the result of adding other gems.
|
|
6
|
-
return if base != ActionController::Base
|
|
7
|
-
end
|
|
8
|
-
|
|
9
9
|
def render(*args)
|
|
10
10
|
options = args.first
|
|
11
11
|
if options.is_a?(Hash) && options.key?(:pdf)
|
|
@@ -29,44 +29,62 @@ class RailsPdfRenderer
|
|
|
29
29
|
def make_pdf(options = {})
|
|
30
30
|
options.delete :pdf # We dont use the filename when rendering to string
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
:formats => options[:formats],
|
|
36
|
-
:handlers => options[:handlers],
|
|
37
|
-
:assigns => options[:assigns]
|
|
38
|
-
}
|
|
39
|
-
render_opts[:inline] = options[:inline] if options[:inline]
|
|
40
|
-
render_opts[:locals] = options[:locals] if options[:locals]
|
|
41
|
-
render_opts[:file] = options[:file] if options[:file]
|
|
42
|
-
html_string = render_to_string(render_opts)
|
|
43
|
-
pdf_from_server(html_string, options)
|
|
32
|
+
return pdf_from_server(raw_html(options), options) if options.key?(:html)
|
|
33
|
+
|
|
34
|
+
pdf_from_server(render_to_string(view_render_options(options)), options)
|
|
44
35
|
end
|
|
45
36
|
|
|
46
37
|
def make_and_send_pdf(pdf_name, options = {})
|
|
47
38
|
options[:layout] ||= false
|
|
48
39
|
options[:template] ||= File.join(controller_path, action_name)
|
|
49
|
-
options[:disposition] ||=
|
|
40
|
+
options[:disposition] ||= "inline"
|
|
50
41
|
if options[:show_as_html]
|
|
51
|
-
render_opts =
|
|
52
|
-
:
|
|
53
|
-
|
|
54
|
-
:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
:content_type => 'text/html'
|
|
58
|
-
}
|
|
59
|
-
render_opts[:inline] = options[:inline] if options[:inline]
|
|
60
|
-
render_opts[:locals] = options[:locals] if options[:locals]
|
|
61
|
-
render_opts[:file] = options[:file] if options[:file]
|
|
42
|
+
render_opts = if options.key?(:html)
|
|
43
|
+
{html: raw_html(options).html_safe, layout: false, content_type: "text/html"}
|
|
44
|
+
else
|
|
45
|
+
view_render_options(options).merge(content_type: "text/html")
|
|
46
|
+
end
|
|
47
|
+
render_opts[:status] = options[:status] if options[:status]
|
|
62
48
|
render(render_opts)
|
|
63
49
|
else
|
|
64
50
|
pdf_content = make_pdf(options)
|
|
65
|
-
File.open(options[:save_to_file],
|
|
66
|
-
|
|
51
|
+
File.open(options[:save_to_file], "wb") { |file| file << pdf_content } if options[:save_to_file]
|
|
52
|
+
unless options[:save_only]
|
|
53
|
+
send_opts = {filename: pdf_name + ".pdf", type: "application/pdf", disposition: options[:disposition]}
|
|
54
|
+
send_opts[:status] = options[:status] if options[:status]
|
|
55
|
+
send_data(pdf_content, send_opts)
|
|
56
|
+
end
|
|
67
57
|
end
|
|
68
58
|
end
|
|
69
59
|
|
|
60
|
+
# Already-rendered HTML supplied by the caller through the :html option. Unlike
|
|
61
|
+
# :inline it is never compiled as a template, so `<%= %>` in the content cannot
|
|
62
|
+
# execute as Ruby on the server.
|
|
63
|
+
def raw_html(options)
|
|
64
|
+
if options.key?(:inline)
|
|
65
|
+
raise ArgumentError, "rails-pdf-renderer: :html and :inline are mutually exclusive. " \
|
|
66
|
+
":html is finished HTML, :inline is an ERB template."
|
|
67
|
+
end
|
|
68
|
+
options[:html].to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Options for rendering through ActionView. Everything here goes through a template
|
|
72
|
+
# handler - :inline defaults to ERB unless :type says otherwise.
|
|
73
|
+
def view_render_options(options)
|
|
74
|
+
render_opts = {
|
|
75
|
+
template: options[:template],
|
|
76
|
+
layout: options[:layout],
|
|
77
|
+
formats: options[:formats],
|
|
78
|
+
handlers: options[:handlers],
|
|
79
|
+
assigns: options[:assigns]
|
|
80
|
+
}
|
|
81
|
+
render_opts[:inline] = options[:inline] if options[:inline]
|
|
82
|
+
render_opts[:locals] = options[:locals] if options[:locals]
|
|
83
|
+
render_opts[:file] = options[:file] if options[:file]
|
|
84
|
+
render_opts[:type] = options[:type] if options[:type]
|
|
85
|
+
render_opts
|
|
86
|
+
end
|
|
87
|
+
|
|
70
88
|
def pdf_from_server(html, options)
|
|
71
89
|
payload = pdf_server_params(html, options)
|
|
72
90
|
|
|
@@ -77,10 +95,10 @@ class RailsPdfRenderer
|
|
|
77
95
|
raise "auth_key is not set, you need to set it for rails-pdf-renderer to work" if auth_key.blank?
|
|
78
96
|
|
|
79
97
|
uri = URI(url)
|
|
80
|
-
headers = {
|
|
98
|
+
headers = {"Content-Type": "application/json", Authorization: "Bearer #{auth_key64}"}
|
|
81
99
|
response = Net::HTTP.post(uri, payload.to_json, headers)
|
|
82
100
|
|
|
83
|
-
if response.
|
|
101
|
+
if response.is_a? Net::HTTPSuccess
|
|
84
102
|
response.body
|
|
85
103
|
else
|
|
86
104
|
raise RailsPdfRenderer::Error.new(response)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require_relative
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "delegate"
|
|
3
|
+
require "stringio"
|
|
4
|
+
require_relative "path_helper"
|
|
5
5
|
|
|
6
6
|
class RailsPdfRenderer
|
|
7
7
|
module ActionViewHelper
|
|
@@ -60,7 +60,7 @@ class RailsPdfRenderer
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def content_type
|
|
63
|
-
Mime::Type.lookup_by_extension(File.extname(path).delete(
|
|
63
|
+
Mime::Type.lookup_by_extension(File.extname(path).delete("."))
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def to_s
|
|
@@ -76,7 +76,7 @@ class RailsPdfRenderer
|
|
|
76
76
|
asset = find_asset(path)
|
|
77
77
|
raise MissingLocalAsset, path if asset.nil?
|
|
78
78
|
|
|
79
|
-
base64 = Base64.encode64(asset.to_s).gsub(/\s+/,
|
|
79
|
+
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
|
|
80
80
|
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
|
|
81
81
|
end
|
|
82
82
|
|
|
@@ -86,7 +86,7 @@ class RailsPdfRenderer
|
|
|
86
86
|
response = Net::HTTP.get_response(URI(url))
|
|
87
87
|
|
|
88
88
|
if response.is_a?(Net::HTTPSuccess)
|
|
89
|
-
base64 = Base64.encode64(response.body).gsub(/\s+/,
|
|
89
|
+
base64 = Base64.encode64(response.body).gsub(/\s+/, "")
|
|
90
90
|
"data:#{response.content_type};base64,#{Rack::Utils.escape(base64)}"
|
|
91
91
|
else
|
|
92
92
|
Rails.logger.warn("[pdf] #{response.code} #{response.message}: #{url}")
|
|
@@ -96,12 +96,12 @@ class RailsPdfRenderer
|
|
|
96
96
|
|
|
97
97
|
def pdf_stylesheet_link_tag(*sources)
|
|
98
98
|
stylesheet_contents = sources.collect do |source|
|
|
99
|
-
source = PathHelper.add_extension(source,
|
|
99
|
+
source = PathHelper.add_extension(source, "css")
|
|
100
100
|
"<style type='text/css'>#{read_asset(source)}</style>"
|
|
101
101
|
end.join("\n")
|
|
102
102
|
|
|
103
103
|
stylesheet_contents.gsub(ASSET_URL_REGEX) do
|
|
104
|
-
if Regexp.last_match[1].starts_with?(
|
|
104
|
+
if Regexp.last_match[1].starts_with?("data:")
|
|
105
105
|
"url(#{Regexp.last_match[1]})"
|
|
106
106
|
else
|
|
107
107
|
"url(#{pdf_asset_path(Regexp.last_match[1])})"
|
|
@@ -116,7 +116,7 @@ class RailsPdfRenderer
|
|
|
116
116
|
stylesheet_pack_tag(*sources)
|
|
117
117
|
else
|
|
118
118
|
css_text = sources.collect do |source|
|
|
119
|
-
source = PathHelper.add_extension(source,
|
|
119
|
+
source = PathHelper.add_extension(source, "css")
|
|
120
120
|
pdf_stylesheet_link_tag(webpacker_source_url(source))
|
|
121
121
|
end.join("\n")
|
|
122
122
|
css_text.respond_to?(:html_safe) ? css_text.html_safe : css_text
|
|
@@ -130,7 +130,7 @@ class RailsPdfRenderer
|
|
|
130
130
|
javascript_pack_tag(*sources)
|
|
131
131
|
else
|
|
132
132
|
sources.collect do |source|
|
|
133
|
-
source = PathHelper.add_extension(source,
|
|
133
|
+
source = PathHelper.add_extension(source, "js")
|
|
134
134
|
"<script type='text/javascript'>#{read_asset(webpacker_source_url(source))}</script>"
|
|
135
135
|
end.join("\n").html_safe
|
|
136
136
|
end
|
|
@@ -141,19 +141,19 @@ class RailsPdfRenderer
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
def pdf_javascript_src_tag(jsfile, options = {})
|
|
144
|
-
jsfile = PathHelper.add_extension(jsfile,
|
|
144
|
+
jsfile = PathHelper.add_extension(jsfile, "js")
|
|
145
145
|
javascript_include_tag pdf_asset_path(jsfile), options
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def pdf_javascript_include_tag(*sources)
|
|
149
149
|
sources.collect do |source|
|
|
150
|
-
source = PathHelper.add_extension(source,
|
|
150
|
+
source = PathHelper.add_extension(source, "js")
|
|
151
151
|
"<script type='text/javascript'>#{read_asset(source)}</script>"
|
|
152
152
|
end.join("\n").html_safe
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
def pdf_asset_path(asset)
|
|
156
|
-
if (pathname = asset_pathname(asset).to_s)
|
|
156
|
+
if URI_REGEXP.match?(pathname = asset_pathname(asset).to_s)
|
|
157
157
|
pathname
|
|
158
158
|
else
|
|
159
159
|
"file:///#{pathname}"
|
|
@@ -179,11 +179,11 @@ class RailsPdfRenderer
|
|
|
179
179
|
if precompiled_or_absolute_asset?(source)
|
|
180
180
|
asset = asset_path(source)
|
|
181
181
|
pathname = prepend_protocol(asset)
|
|
182
|
-
if pathname
|
|
182
|
+
if URI_REGEXP.match?(pathname)
|
|
183
183
|
# asset_path returns an absolute URL using asset_host if asset_host is set
|
|
184
184
|
pathname
|
|
185
185
|
else
|
|
186
|
-
File.join(Rails.public_path, asset.sub(/\A#{Rails.application.config.action_controller.relative_url_root}/,
|
|
186
|
+
File.join(Rails.public_path, asset.sub(/\A#{Rails.application.config.action_controller.relative_url_root}/, ""))
|
|
187
187
|
end
|
|
188
188
|
else
|
|
189
189
|
asset = find_asset(source)
|
|
@@ -198,7 +198,7 @@ class RailsPdfRenderer
|
|
|
198
198
|
|
|
199
199
|
def find_asset(path)
|
|
200
200
|
if Rails.application.assets.respond_to?(:find_asset)
|
|
201
|
-
Rails.application.assets.find_asset(path, :
|
|
201
|
+
Rails.application.assets.find_asset(path, base_path: Rails.application.root.to_s)
|
|
202
202
|
elsif defined?(Propshaft::Assembly) && Rails.application.assets.is_a?(Propshaft::Assembly)
|
|
203
203
|
PropshaftAsset.new(Rails.application.assets.load_path.find(path))
|
|
204
204
|
elsif Rails.application.respond_to?(:assets_manifest)
|
|
@@ -208,7 +208,7 @@ class RailsPdfRenderer
|
|
|
208
208
|
asset_path = File.join(Rails.application.assets_manifest.dir, relative_asset_path)
|
|
209
209
|
LocalAsset.new(asset_path) if File.file?(asset_path)
|
|
210
210
|
else
|
|
211
|
-
SprocketsEnvironment.find_asset(path, :
|
|
211
|
+
SprocketsEnvironment.find_asset(path, base_path: Rails.application.root.to_s)
|
|
212
212
|
end
|
|
213
213
|
end
|
|
214
214
|
|
|
@@ -219,7 +219,7 @@ class RailsPdfRenderer
|
|
|
219
219
|
assets.find do |asset, _v|
|
|
220
220
|
directory = File.dirname(asset)
|
|
221
221
|
asset_path = File.basename(asset, File.extname(asset))
|
|
222
|
-
asset_path = File.join(directory, asset_path) if directory !=
|
|
222
|
+
asset_path = File.join(directory, asset_path) if directory != "."
|
|
223
223
|
|
|
224
224
|
asset_path == path
|
|
225
225
|
end&.last
|
|
@@ -231,11 +231,11 @@ class RailsPdfRenderer
|
|
|
231
231
|
# will prepend a http or default_protocol to a protocol relative URL
|
|
232
232
|
# or when no protcol is set.
|
|
233
233
|
def prepend_protocol(source)
|
|
234
|
-
protocol = RailsPdfRenderer.configuration.default_protocol ||
|
|
235
|
-
if source[0, 2] ==
|
|
236
|
-
source = [protocol,
|
|
237
|
-
elsif source[0] !=
|
|
238
|
-
source = [protocol,
|
|
234
|
+
protocol = RailsPdfRenderer.configuration.default_protocol || "http"
|
|
235
|
+
if source[0, 2] == "//"
|
|
236
|
+
source = [protocol, ":", source].join
|
|
237
|
+
elsif source[0] != "/" && !source[0, 8].include?("://")
|
|
238
|
+
source = [protocol, "://", source].join
|
|
239
239
|
end
|
|
240
240
|
source
|
|
241
241
|
end
|
|
@@ -243,13 +243,13 @@ class RailsPdfRenderer
|
|
|
243
243
|
def precompiled_or_absolute_asset?(source)
|
|
244
244
|
!Rails.configuration.respond_to?(:assets) ||
|
|
245
245
|
Rails.configuration.assets.compile == false ||
|
|
246
|
-
source.to_s[0] ==
|
|
247
|
-
source.to_s.match(/\Ahttps
|
|
246
|
+
source.to_s[0] == "/" ||
|
|
247
|
+
source.to_s.match(/\Ahttps?:\/\//)
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
def read_asset(source)
|
|
251
251
|
asset = find_asset(source)
|
|
252
|
-
return asset.to_s.force_encoding(
|
|
252
|
+
return asset.to_s.force_encoding("UTF-8") if asset
|
|
253
253
|
|
|
254
254
|
unless precompiled_or_absolute_asset?(source)
|
|
255
255
|
raise MissingLocalAsset, source if RailsPdfRenderer.configuration.raise_on_missing_assets
|
|
@@ -258,7 +258,7 @@ class RailsPdfRenderer
|
|
|
258
258
|
end
|
|
259
259
|
|
|
260
260
|
pathname = asset_pathname(source)
|
|
261
|
-
if pathname
|
|
261
|
+
if URI_REGEXP.match?(pathname)
|
|
262
262
|
read_from_uri(pathname)
|
|
263
263
|
elsif File.file?(pathname)
|
|
264
264
|
IO.read(pathname)
|
|
@@ -277,7 +277,7 @@ class RailsPdfRenderer
|
|
|
277
277
|
end
|
|
278
278
|
|
|
279
279
|
asset = response.body
|
|
280
|
-
asset
|
|
280
|
+
asset&.force_encoding("UTF-8")
|
|
281
281
|
asset = gzip(asset) if RailsPdfRenderer.configuration.expect_gzipped_remote_assets
|
|
282
282
|
asset
|
|
283
283
|
end
|
|
@@ -294,9 +294,9 @@ class RailsPdfRenderer
|
|
|
294
294
|
return unless webpacker_version
|
|
295
295
|
|
|
296
296
|
# In Webpacker 3.2.0 asset_pack_url is introduced
|
|
297
|
-
if webpacker_version >=
|
|
297
|
+
if webpacker_version >= "3.2.0"
|
|
298
298
|
if (host = Rails.application.config.asset_host)
|
|
299
|
-
asset_pack_path(source, :
|
|
299
|
+
asset_pack_path(source, host: host)
|
|
300
300
|
else
|
|
301
301
|
asset_pack_url(source)
|
|
302
302
|
end
|
|
@@ -320,12 +320,12 @@ class RailsPdfRenderer
|
|
|
320
320
|
|
|
321
321
|
def webpacker_version
|
|
322
322
|
if defined?(Shakapacker)
|
|
323
|
-
require
|
|
323
|
+
require "shakapacker/version"
|
|
324
324
|
Shakapacker::VERSION
|
|
325
325
|
elsif defined?(Webpacker)
|
|
326
|
-
require
|
|
326
|
+
require "webpacker/version"
|
|
327
327
|
Webpacker::VERSION
|
|
328
328
|
end
|
|
329
329
|
end
|
|
330
330
|
end
|
|
331
|
-
end
|
|
331
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
class RailsPdfRenderer
|
|
2
2
|
module PathHelper
|
|
3
3
|
def self.add_extension(filename, extension)
|
|
4
|
-
filename.to_s.split(
|
|
4
|
+
filename.to_s.split(".").include?(extension) ? filename : "#{filename}.#{extension}"
|
|
5
5
|
end
|
|
6
6
|
end
|
|
7
|
-
end
|
|
7
|
+
end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
require_relative
|
|
2
|
-
require_relative
|
|
1
|
+
require_relative "action_controller_helper"
|
|
2
|
+
require_relative "action_view_helper"
|
|
3
3
|
|
|
4
4
|
class RailsPdfRenderer
|
|
5
5
|
if defined?(Rails.env)
|
|
6
6
|
class RailsPdfRendererRailtie < Rails::Railtie
|
|
7
|
-
initializer
|
|
7
|
+
initializer "rails-pdf-renderer.register" do |_app|
|
|
8
8
|
ActiveSupport.on_load(:action_controller) { ActionController::Base.send :prepend, RailsPdfRenderer::ActionControllerHelper }
|
|
9
9
|
ActiveSupport.on_load(:action_view) { include RailsPdfRenderer::ActionViewHelper }
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
Mime::Type.register(
|
|
13
|
+
Mime::Type.register("application/pdf", :pdf) if Mime::Type.lookup_by_extension(:pdf).nil?
|
|
14
14
|
end
|
|
15
15
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-pdf-renderer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Axel Nielsen
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activesupport
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '7.0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: rails
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,8 +100,13 @@ executables: []
|
|
|
86
100
|
extensions: []
|
|
87
101
|
extra_rdoc_files: []
|
|
88
102
|
files:
|
|
103
|
+
- ".claude/skills/commit/SKILL.md"
|
|
104
|
+
- ".claude/skills/create-pr/SKILL.md"
|
|
105
|
+
- ".claude/skills/release/SKILL.md"
|
|
89
106
|
- ".rspec"
|
|
107
|
+
- ".tool-versions"
|
|
90
108
|
- CHANGELOG.md
|
|
109
|
+
- CLAUDE.md
|
|
91
110
|
- LICENSE.txt
|
|
92
111
|
- README.md
|
|
93
112
|
- Rakefile
|
|
@@ -99,7 +118,6 @@ files:
|
|
|
99
118
|
- lib/rails/pdf/renderer/path_helper.rb
|
|
100
119
|
- lib/rails/pdf/renderer/railtie.rb
|
|
101
120
|
- lib/rails/pdf/renderer/version.rb
|
|
102
|
-
- rails-pdf-renderer.gemspec
|
|
103
121
|
homepage: https://github.com/erikaxel/
|
|
104
122
|
licenses:
|
|
105
123
|
- MIT
|
|
@@ -114,14 +132,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
132
|
requirements:
|
|
115
133
|
- - ">="
|
|
116
134
|
- !ruby/object:Gem::Version
|
|
117
|
-
version:
|
|
135
|
+
version: 3.3.0
|
|
118
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
137
|
requirements:
|
|
120
138
|
- - ">="
|
|
121
139
|
- !ruby/object:Gem::Version
|
|
122
140
|
version: '0'
|
|
123
141
|
requirements: []
|
|
124
|
-
rubygems_version: 4.0.
|
|
142
|
+
rubygems_version: 4.0.16
|
|
125
143
|
specification_version: 4
|
|
126
144
|
summary: Create PDFs directly from Rails.
|
|
127
145
|
test_files: []
|
data/rails-pdf-renderer.gemspec
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/rails/pdf/renderer/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "rails-pdf-renderer"
|
|
7
|
-
spec.version = RailsPdfRenderer::VERSION
|
|
8
|
-
spec.authors = ["Erik Axel Nielsen"]
|
|
9
|
-
spec.email = ["erikaxel.nielsen@gmail.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "Create PDFs directly from Rails."
|
|
12
|
-
spec.description = "Helper library to generate PDFs from Rails."
|
|
13
|
-
spec.homepage = "https://github.com/erikaxel/"
|
|
14
|
-
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 2.4.0"
|
|
16
|
-
|
|
17
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
-
spec.metadata["source_code_uri"] = "https://github.com/erikaxel/"
|
|
19
|
-
spec.metadata["changelog_uri"] = "https://github.com/erikaxel/CHANGELOG.md"
|
|
20
|
-
|
|
21
|
-
# Specify which files should be added to the gem when it is released.
|
|
22
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
23
|
-
spec.files = Dir.chdir(__dir__) do
|
|
24
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
25
|
-
(File.expand_path(f) == __FILE__) ||
|
|
26
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
spec.bindir = "exe"
|
|
30
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
|
-
spec.require_paths = ["lib"]
|
|
32
|
-
|
|
33
|
-
spec.add_development_dependency 'rails'
|
|
34
|
-
spec.add_development_dependency 'rake'
|
|
35
|
-
spec.add_development_dependency 'rspec'
|
|
36
|
-
spec.add_development_dependency "rspec-rails"
|
|
37
|
-
spec.add_development_dependency 'standard'
|
|
38
|
-
end
|