pdfh 4.0.1 → 4.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 833d681fc080cc70f16c365322eb3d6c9acdccfcc817ccd8cac317fa89b03b31
4
- data.tar.gz: d818e4a7f2b65d06362525b5ecb94d534422d78ae8fa60357109603be6629467
3
+ metadata.gz: a5ef9e4932b73970339e621800eaadd8de7c0bbc07c4536ea4f360fc5227c27a
4
+ data.tar.gz: 9b02deb23b1522d0debb7a9b480b82b7e69c3bedf286743cd3e7a4c065bc6e97
5
5
  SHA512:
6
- metadata.gz: 629936ffe78b2a1a9d8e405ac5ed1de5a7c4c2ae2022d8722888fa467e75b674961e51f381f7d5c62698a4b480a9405190f79ff57f6fe9ba38e0d781273590dc
7
- data.tar.gz: 3a3658de5d2775ba4a5503847f374e82d42fc73c64cffe5d5fb723ee73953c1d027f27325be9b4e7f31be7766f8f6b715515c1623fbb381b528bd2489c6e7c14
6
+ metadata.gz: 9f694edb6bbe90f5cccb66d94bdf901983b9824f9de71e444065fa98aab75da4ab951965aed0b18de2246c60768e223035294b34d2c3459c251a5a7df2f13678
7
+ data.tar.gz: e2253c53cf4936b9422465ba0bbdf5a7a59b0fc8c0ec8b4bbb63e0bac5d613d80d8ccb490c218d6f7efc09eb16b077e9d13e65c958e6c3ce099cd0b905bf2280
@@ -25,12 +25,6 @@ repos:
25
25
  hooks:
26
26
  - id: gitleaks
27
27
 
28
- - repo: https://github.com/rubocop/rubocop
29
- rev: v1.86.0
30
- hooks:
31
- - id: rubocop
32
- args: [--parallel]
33
-
34
28
  - repo: https://github.com/compilerla/conventional-pre-commit
35
29
  rev: v4.4.0
36
30
  hooks:
data/AGENTS.md CHANGED
@@ -43,10 +43,18 @@ Useful `mise` tasks (`mise.toml`):
43
43
  - `mise run test` (runs `bundle exec rspec --order random --format documentation`)
44
44
  - `mise run check` (runs lint + test)
45
45
  - `mise run bump[major|minor|tiny]`
46
- - `mise run pre` (runs `pre-commit run --all-files`)
46
+ - `mise run pre` (runs `pre-commit run --all-files`; also runs `gitleaks` and `codespell`, which can block a commit independently of RuboCop/tests)
47
47
  - `mise run update` (runs `bundle update`)
48
48
  - `mise run release` (builds gem + pushes to RubyGems)
49
49
 
50
+ Equivalent `just` recipes (`justfile`) are also available and wrap the same commands:
51
+
52
+ - `just check` (lint + test)
53
+ - `just test` / `just test-file spec/path_spec.rb:42`
54
+ - `just lint` (runs `rubocop -a`, auto-fixes)
55
+ - `just bump [major|minor|tiny]`
56
+ - `just release`
57
+
50
58
  ## Repository Layout
51
59
 
52
60
  - `exe/pdfh` CLI entrypoint
@@ -59,7 +67,11 @@ Useful `mise` tasks (`mise.toml`):
59
67
  ## Processing Flow (High Level)
60
68
 
61
69
  ```text
62
- exe/pdfh → Main.start OptParserSettingsBuilder → SettingsValidator
70
+ exe/pdfh → DependencyValidator (qpdf, pdftotext)Main.start
71
+
72
+ OptParser (ARGV)
73
+
74
+ SettingsBuilder → SettingsValidator
63
75
 
64
76
  DirectoryScanner.scan (finds *.pdf)
65
77
 
@@ -76,9 +88,9 @@ Key service objects in `lib/pdfh/services/`:
76
88
  - `PdfTextExtractor` — shells out `pdftotext -enc UTF-8 -layout`
77
89
  - `DocumentMatcher` — iterates `DocumentType`s; matches `re_id` then `re_date`
78
90
  - `DocumentManager` — creates dest dir, copies/renames PDF, copies companion files
79
- - `SettingsBuilder` — locates and parses `pdfh.yml`
91
+ - `SettingsBuilder` — resolves config from env/known locations (`.yml`/`.yaml`), validates, and builds `Settings`
80
92
  - `SettingsValidator` — validates settings; raises `ArgumentError` on fatal errors, warns and skips on soft errors (e.g. missing lookup dirs)
81
- - `OptParser` — parses `ARGV` into `RunOptions`
93
+ - `OptParser` — parses `ARGV` into `RunOptions`; supports `--list-types`, `-v/--verbose`, `-d/--dry`, `-V/--version`, `-h/--help`
82
94
 
83
95
  ## Code Style and Conventions
84
96
 
@@ -127,6 +139,7 @@ Side effects and IO:
127
139
  - File system changes are centralized in `DocumentManager`.
128
140
  - Respect `dry_run` / `--dry` to avoid writing files.
129
141
  - When copying/moving files, preserve metadata if possible (`FileUtils` with `preserve: true`).
142
+ - After successful processing, source PDFs are moved to `*.bkp` in place (`DocumentManager#backup_original`).
130
143
 
131
144
  Testing:
132
145
 
@@ -136,7 +149,9 @@ Testing:
136
149
 
137
150
  ## Configuration and Templates
138
151
 
139
- - Configuration is loaded from `$PDFH_CONFIG_FILE`, then CWD, `~/.config/`, then `~/`.
152
+ - Configuration is loaded from `$PDFH_CONFIG_FILE`, then CWD, then `$XDG_CONFIG_HOME` (default `~/.config`), then `~/`.
153
+ - Supported config file extensions are `.yml` and `.yaml`.
154
+ - If no config file is found, `SettingsBuilder` creates a default template at `~/pdfh.yml`.
140
155
  - Document templates use `{placeholder}` tokens defined in `Document#rename_data`.
141
156
  - Date regex supports named captures `(?<m>...)` / `(?<y>...)` / `(?<d>...)` or positional captures.
142
157
 
@@ -149,8 +164,10 @@ Testing:
149
164
  - **v4 breaking change**: Config format changed in v4.0.0 — old configs are incompatible. `re_id` matches PDF *text content* (not filename). New placeholders: `{day}`, `{quarter}`, `{bimester}`, `{name}`.
150
165
  - **Multi-match behavior**: PDFs matching more than one `DocumentType` are silently skipped (see `Main.start`).
151
166
  - **`_unlocked` suffix**: `DocumentManager` strips `_unlocked` when locating companion files — legacy pattern from `qpdf` unlocking.
152
- - **Config search path**: `$PDFH_CONFIG_FILE` CWD `~/.config/` `~/`. Error message does not list paths checked.
167
+ - **First run side effect**: Missing config does not fail fast; it auto-creates `~/pdfh.yml` and continues from that template.
168
+ - **Dependency mismatch risk**: `qpdf` is still checked at startup (`exe/pdfh`) even though normal v4 processing does not call it directly.
153
169
  - **Colorize in tests**: Logging output is colored via `colorize`; specs mock `Console` with shared context `"with silent console"` to suppress output.
170
+ - **CI only lints, not tests**: `.github/workflows/rubocop-analysis.yml` is the only CI workflow and runs RuboCop only (via CodeQL/SARIF upload); RSpec is not run in CI. Always run `mise run test` locally — a green CI check does not mean tests pass.
154
171
 
155
172
  ## Suggested Workflow for Agents
156
173
 
@@ -159,6 +176,10 @@ Testing:
159
176
  - Run `mise run lint` and `mise run test` before finalizing changes.
160
177
  - Keep changes localized; avoid touching unrelated files.
161
178
 
179
+ ## Repo Etiquette
180
+
181
+ - Commit messages must follow Conventional Commits format (e.g. `fix:`, `feat:`, `chore:`) — enforced by the `conventional-pre-commit` `commit-msg` hook in `.pre-commit-config.yaml`. Non-conforming messages are rejected.
182
+
162
183
  ## Examples
163
184
 
164
185
  Run a single spec:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## v4.0.3 - 2026-09-21
4
+
5
+ - Centralize `_unlocked` suffix handling in `FileInfo#stem`
6
+ - Upgrade gems
7
+
8
+ ## v4.0.2 - 2026-08-03
9
+
10
+ - Skip `.bkp` files when copying companion files
11
+
12
+ ## v4.0.1 - 2026-03-24
13
+
14
+ - Fix incorrect `require`
15
+
3
16
  ## v4.0.0 - 2026-03-21
4
17
 
5
18
  ### Breaking
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pdfh (4.0.1)
4
+ pdfh (4.0.3)
5
5
  colorize (~> 1.1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (8.1.3)
10
+ activesupport (8.1.3.1)
11
11
  base64
12
12
  bigdecimal
13
13
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -22,57 +22,58 @@ GEM
22
22
  uri (>= 0.13.1)
23
23
  ast (2.4.3)
24
24
  base64 (0.3.0)
25
- bigdecimal (4.0.1)
25
+ bigdecimal (4.1.3)
26
26
  code-scanning-rubocop (0.6.1)
27
27
  rubocop (~> 1.0)
28
28
  colorize (1.1.0)
29
- concurrent-ruby (1.3.6)
29
+ concurrent-ruby (1.3.8)
30
30
  connection_pool (3.0.2)
31
- date (3.5.1)
32
31
  debug (1.11.1)
33
32
  irb (~> 1.10)
34
33
  reline (>= 0.3.8)
35
34
  diff-lcs (1.6.2)
36
35
  docile (1.4.1)
37
36
  drb (2.2.3)
38
- erb (6.0.2)
39
- factory_bot (6.5.6)
37
+ erb (6.0.7)
38
+ factory_bot (6.6.0)
40
39
  activesupport (>= 6.1.0)
41
- i18n (1.14.8)
40
+ i18n (1.15.2)
42
41
  concurrent-ruby (~> 1.0)
43
- io-console (0.8.2)
44
- irb (1.17.0)
42
+ io-console (0.9.4)
43
+ irb (1.18.0)
45
44
  pp (>= 0.6.0)
46
45
  prism (>= 1.3.0)
47
46
  rdoc (>= 4.0.0)
48
47
  reline (>= 0.4.2)
49
- json (2.19.2)
50
- language_server-protocol (3.17.0.5)
48
+ json (3.0.2)
49
+ language_server-protocol (3.17.0.6)
51
50
  lint_roller (1.1.0)
52
51
  logger (1.7.0)
53
- minitest (6.0.2)
52
+ minitest (6.0.6)
54
53
  drb (~> 2.0)
55
54
  prism (~> 1.5)
56
- parallel (1.27.0)
57
- parser (3.3.10.2)
55
+ parallel (2.2.0)
56
+ parser (3.3.12.0)
58
57
  ast (~> 2.4.1)
59
58
  racc
60
- pp (0.6.3)
59
+ pp (0.6.4)
61
60
  prettyprint
62
61
  prettyprint (0.2.0)
63
62
  prism (1.9.0)
64
- psych (5.3.1)
65
- date
66
- stringio
67
63
  racc (1.8.1)
68
64
  rainbow (3.1.1)
69
- rake (13.3.1)
70
- rdoc (7.2.0)
65
+ rake (13.4.2)
66
+ rbs (4.2.0)
67
+ logger
68
+ prism (>= 1.6.0)
69
+ tsort
70
+ rdoc (8.0.0)
71
71
  erb
72
- psych (>= 4.0.0)
72
+ prism (>= 1.6.0)
73
+ rbs (>= 4.0.0)
73
74
  tsort
74
- regexp_parser (2.11.3)
75
- reline (0.6.3)
75
+ regexp_parser (2.13.0)
76
+ reline (0.7.0)
76
77
  io-console (~> 0.5)
77
78
  rspec (3.13.2)
78
79
  rspec-core (~> 3.13.0)
@@ -87,33 +88,34 @@ GEM
87
88
  diff-lcs (>= 1.2.0, < 2.0)
88
89
  rspec-support (~> 3.13.0)
89
90
  rspec-support (3.13.7)
90
- rubocop (1.86.0)
91
- json (~> 2.3)
91
+ rubocop (1.91.0)
92
+ json (>= 2.3)
92
93
  language_server-protocol (~> 3.17.0.2)
93
94
  lint_roller (~> 1.1.0)
94
- parallel (~> 1.10)
95
+ parallel (>= 1.10)
95
96
  parser (>= 3.3.0.2)
96
97
  rainbow (>= 2.2.2, < 4.0)
97
98
  regexp_parser (>= 2.9.3, < 3.0)
98
99
  rubocop-ast (>= 1.49.0, < 2.0)
99
100
  ruby-progressbar (~> 1.7)
100
101
  unicode-display_width (>= 2.4.0, < 4.0)
101
- rubocop-ast (1.49.1)
102
+ rubocop-ast (1.50.0)
102
103
  parser (>= 3.3.7.2)
103
104
  prism (~> 1.7)
104
105
  rubocop-factory_bot (2.28.0)
105
106
  lint_roller (~> 1.1)
106
107
  rubocop (~> 1.72, >= 1.72.1)
107
- rubocop-performance (1.26.1)
108
+ rubocop-performance (1.27.0)
108
109
  lint_roller (~> 1.1)
109
- rubocop (>= 1.75.0, < 2.0)
110
+ rubocop (>= 1.89.0, < 2.0)
110
111
  rubocop-ast (>= 1.47.1, < 2.0)
111
112
  rubocop-rake (0.7.1)
112
113
  lint_roller (~> 1.1)
113
114
  rubocop (>= 1.72.1)
114
- rubocop-rspec (3.9.0)
115
+ rubocop-rspec (3.10.2)
115
116
  lint_roller (~> 1.1)
116
- rubocop (~> 1.81)
117
+ regexp_parser (>= 2.0)
118
+ rubocop (~> 1.86, >= 1.86.2)
117
119
  ruby-progressbar (1.13.0)
118
120
  securerandom (0.4.1)
119
121
  simplecov (0.22.0)
@@ -122,13 +124,12 @@ GEM
122
124
  simplecov_json_formatter (~> 0.1)
123
125
  simplecov-html (0.13.2)
124
126
  simplecov_json_formatter (0.1.4)
125
- stringio (3.2.0)
126
127
  tsort (0.2.0)
127
128
  tzinfo (2.0.6)
128
129
  concurrent-ruby (~> 1.0)
129
- unicode-display_width (3.2.0)
130
- unicode-emoji (~> 4.1)
131
- unicode-emoji (4.2.0)
130
+ unicode-display_width (3.3.0)
131
+ unicode-emoji (~> 4.3)
132
+ unicode-emoji (4.3.0)
132
133
  uri (1.1.1)
133
134
 
134
135
  PLATFORMS
@@ -150,4 +151,4 @@ DEPENDENCIES
150
151
  simplecov (~> 0.22)
151
152
 
152
153
  BUNDLED WITH
153
- 4.0.9
154
+ 4.0.21
@@ -4,8 +4,6 @@ module Pdfh
4
4
  module Services
5
5
  # Manages the documents, rename, move, etc.
6
6
  class DocumentManager
7
- PDF_UNLOCKED_MAGIC_SUFFIX = "_unlocked"
8
-
9
7
  # @param document [Document]
10
8
  # @param base_path [String]
11
9
  # @param dry_run [Boolean]
@@ -86,19 +84,22 @@ module Pdfh
86
84
  FileUtils.mv(source_file, backup_file) unless dry_run?
87
85
  end
88
86
 
89
- # Finds companion files by removing the _unlocked suffix from the PDF name if present.
90
- # This allows PDFs unlock by qpdf to locate their original companion files (e.g., .xml, .txt)
91
- # that were never renamed with the _unlocked suffix.
87
+ # Finds companion files by using the document's clean stem, which already has the
88
+ # `_unlocked` suffix removed (see Pdfh::FileInfo#stem). This allows PDFs unlocked by
89
+ # qpdf to locate their original companion files (e.g., .xml, .txt) that were never
90
+ # renamed with the `_unlocked` suffix.
91
+ # Excludes PDF files and `.bkp` backup files (already-processed PDFs renamed by +backup_original+).
92
92
  #
93
- # @return [Array<String>] array of non-PDF files with the same base name
93
+ # @return [Array<String>] array of non-PDF, non-backup files with the same base name
94
94
  # @example
95
95
  # # If document is "cuenta_unlocked.pdf", searches for "cuenta.*"
96
- # # Returns ["cuenta.xml", "cuenta.txt"] (excluding "cuenta.pdf")
96
+ # # Returns ["cuenta.xml", "cuenta.txt"] (excluding "cuenta.pdf" and "cuenta.pdf.bkp")
97
97
  def companion_files
98
98
  @companion_files ||= begin
99
- base_name = document.file_info.stem.delete_suffix(PDF_UNLOCKED_MAGIC_SUFFIX)
99
+ base_name = document.file_info.stem
100
100
  Dir.glob(File.join(document.file_info.dir, "#{base_name}.*")).reject do |file|
101
- File.extname(file) == ".pdf"
101
+ ext = File.extname(file)
102
+ ext == ".pdf" || file.end_with?(".bkp")
102
103
  end
103
104
  end
104
105
  end
@@ -4,6 +4,10 @@ module Pdfh
4
4
  # Encapsulates file system metadata for a PDF file path.
5
5
  # Responsible for all file name derivations (extension, base name, backup name, etc.).
6
6
  class FileInfo
7
+ # Magic suffix added by qpdf when unlocking a PDF. Treated as noise: it is
8
+ # never part of the real document name.
9
+ PDF_UNLOCKED_MAGIC_SUFFIX = "_unlocked"
10
+
7
11
  # @param file_path [String] Absolute or relative path to the PDF file
8
12
  # @return [self]
9
13
  def initialize(file_path)
@@ -13,9 +17,9 @@ module Pdfh
13
17
  # @return [String] Full path to the file
14
18
  attr_reader :path
15
19
 
16
- # @return [String] File name without extension (e.g., "cuenta")
20
+ # @return [String] File name without extension or the `_unlocked` magic suffix (e.g., "cuenta")
17
21
  def stem
18
- File.basename(@path, extension)
22
+ File.basename(@path, extension).delete_suffix(PDF_UNLOCKED_MAGIC_SUFFIX)
19
23
  end
20
24
  alias name_only stem
21
25
 
data/lib/pdfh/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pdfh
4
- VERSION = "4.0.1"
4
+ VERSION = "4.0.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdfh
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Isaias Piña
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 4.0.9
101
+ rubygems_version: 4.0.21
102
102
  specification_version: 4
103
103
  summary: Organize PDF files
104
104
  test_files: []