jekyll-documents 0.1.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 518470aad866ef5e32081999ae94d7d8bc3ab87a1f19240a66a2152b13a7db87
4
- data.tar.gz: 392d655c0163d3274b3b9e679009aa66d89ab87802e359a5c520b05fdb666897
3
+ metadata.gz: 3865d4ab22aa0cf85e53ad31d222e4e4d025f7f53fb1271a949d326ee8ad60a3
4
+ data.tar.gz: 483721501a072d99ade0088e958b52e02583b4d13de7ee26ecd74bc7141b9c87
5
5
  SHA512:
6
- metadata.gz: c1152712cbdef7b491ab32618bee51ffc7fa02d45f92eff6e92c1d5fc07ba7b00f787550e18a0554e3909b0e96cfb64edb6102a9b5477f9e1da40b44df4f4a76
7
- data.tar.gz: 40b4cc7ff588faf52d29b59229c82f3088d1063495640c3a86c3bbb1b7e1d042a2f1014b065bf6bd3e0713c760d772245f37e3491bdb3cc44e56d37030cddbe5
6
+ metadata.gz: c923ebb2b6e801ab1449b76ed999e8408d234ddb03c03c5b6bf21086c54a49a6fbc7d74b258049248ae53c159c81814d7f23ee44e1f590d9c89f49814bf56630
7
+ data.tar.gz: 639afd3c1abce1196a31b7fb7e34acc197d681f5dc863c53b480f7d127b104db0e27c9adb02c88889dcc62977f4d86162bfade3a077ea269f811e0f24a6da3da
data/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+
4
+ ## [0.3.0] - 2026-07-23
5
+
6
+ ### Added
7
+ - **Baked icon data**: Generator now bakes `icon_url` and `icon_set` into each document's data at build time, eliminating the need for Liquid context in filters
8
+ - **`FileTypeIcons.icon_for` class method**: Allows icon URL resolution without a Liquid context (used by generator)
9
+ - **Search JavaScript wired up**: `_includes/documents_search.html` now loads Lunr.js and `documents-search.js` for out-of-the-box client-side search
10
+ - **jekyll-sitemap integration**: `TextStaticFile` exposes `sitemap: false` data to exclude generated JSON index from sitemap
11
+ - **Unit tests for `FileTypeIcons.icon_for`**: 5 new tests covering all icon sets, fallbacks, and nil handling
12
+ - **Generator tests for icon data**: Tests verifying `icon_url` and `icon_set` are correctly baked into document data
13
+ - **System test for full build pipeline**: End-to-end test covering generator → JSON index → tag rendering
14
+
15
+ ### Changed
16
+ - **Minimum Ruby version**: Bumped from `>= 2.7` to `>= 3.1` (Ruby 2.7 EOL March 2023, 3.0 EOL April 2024)
17
+ - **RuboCop `TargetRubyVersion`**: Updated from `2.7` to `3.1` to match gemspec
18
+ - **Templates use baked icon data**: `latest_documents.html`, `documents_list.html`, `document.html` now use `{{ doc.icon_url }}` instead of the broken `file_type_icon_tag` filter
19
+ - **`category_list.html` icon set**: Now reads `icon_set` from first document's baked data instead of non-functional `site.documents.icon_set`
20
+ - **`latest_documents.html` count fallback**: Simplified to `| default: 5` since `site.config` is inaccessible in Liquid
21
+ - **Reek configuration**: Updated `.reek.yml` with targeted exclusions for design-inherent warnings
22
+ - **Code quality**: Renamed uncommunicative variables (`s` → `slug`/`result`, `d` → `doc`, `m` → `match`, `y/m/d` → `year/month/day`) across all source files
23
+ - **Reduced duplicate method calls**: Extracted `data = doc.data` locals in generator, JSON index generator, and latest documents tag
24
+ - **Simplified `get_icon_set`**: Reduced from 6 statements to 3 using safe navigation with `dig`
25
+ - **Test suite**: 167 examples (up from 79), 100% line coverage (212/212 lines)
26
+ - **RuboCop**: 0 offenses (24 files inspected)
27
+ - **Reek**: 0 warnings (7 files inspected)
28
+
29
+ ### Fixed
30
+ - **Icon set config ignored in templates** (High): `file_type_icon_tag` filter never received Liquid context, causing `icon_set` config to be silently ignored — always fell back to `"color"`. Fixed by baking icon URLs into document data at generation time
31
+ - **`rel_path` slicing** (Low): Replaced fragile `path[(site.source.length + 1)..]` with `String#delete_prefix` for robustness
32
+ - **`Date.parse` in `parse_filename`** (Low): Replaced with `Date.new(year, month, day)` using integer-converted regex captures for clarity
33
+ - **`category_list.html` folder icon** (Low): `site.documents.icon_set` always resolved to `nil` since `site.documents` is an array — now reads from baked document data
34
+ - **Search include missing JavaScript** (Medium): `documents_search.html` had input/results elements but no script tags — now loads Lunr.js and search JS
35
+ - **No active jekyll-sitemap integration** (Low): JSON index file now flagged with `sitemap: false` to exclude it from sitemap
36
+
37
+ ## [0.2.0] - 2026-03-12
38
+
39
+ ### Added
40
+ - **Comprehensive code quality tools** (RuboCop, Reek, Bundler Audit, SimpleCov)
41
+ - **98.99% test coverage** with integration tests for Liquid tags
42
+ - **Enhanced release automation** with 10 safety checks and validations
43
+ - **Pre-commit hooks** for automatic quality checks
44
+ - **Dry-run mode** for release preview
45
+ - **Rollback capability** for undoing releases
46
+ - **Automated CHANGELOG templates** when bumping versions
47
+ - **Release notes generator** with clipboard integration
48
+ - **Git status and dependency checks** before release
49
+ - **Version consistency validation** across files
50
+ - **Post-release verification** with helpful links
51
+
52
+ ### Changed
53
+ - **Consolidated documentation** (README.Development.md combines all dev docs)
54
+ - **Simplified README.md** with KISS approach
55
+ - **Improved Rake tasks** with better organization and help system
56
+ - **Enhanced test suite** with 79 passing tests
57
+ - **Better error handling** in release scripts
58
+
59
+ ### Fixed
60
+ - **Liquid tag testing** through integration tests (resolves 98% coverage)
61
+ - **Keyword argument compatibility** in filters
62
+ - **Release script safety** with comprehensive validation
63
+ - **Documentation consistency** across all markdown files
64
+
65
+ ### Development
66
+ - **Quality metrics**: 98.99% coverage • 0 vulnerabilities • 3 RuboCop offenses
67
+ - **Release workflow**: Fully automated with rollback capability
68
+ - **Testing**: 79 examples, 0 failures, integration tests for all features
69
+
70
+ ## [0.1.2] - 2026-03-09
71
+
72
+ ### Added
73
+ - **Release automation scripts**
74
+
3
75
  ## [0.1.1] - 2026-03-09
4
76
 
5
77
  ### Added
data/README.md CHANGED
@@ -1,13 +1,8 @@
1
1
  # jekyll-documents
2
2
 
3
- Turn files in `assets/documents/` into browsable document pages with icons, categories, and search.
3
+ Turn files in `assets/documents/` into browsable document pages.
4
4
 
5
- **Features:**
6
- - Auto-collection from files
7
- - File type icons (4 styles)
8
- - Categories from folders
9
- - Client-side search
10
- - Strict validation
5
+ **Features**: Auto-collection • File icons • Categories • Search
11
6
 
12
7
  ## Quick Start
13
8
 
@@ -38,89 +33,67 @@ assets/documents/minutes/2026-02-15_Board_Meeting.docx
38
33
  {% include category_list.html %}
39
34
  ```
40
35
 
41
- ## Requirements
42
-
43
- Ruby >= 2.7, Jekyll >= 4.0
44
36
  ## File Naming
45
37
 
46
- **Format:** `YYYY-MM-DD_Title_With_Underscores.ext`
38
+ **Format**: `YYYY-MM-DD_Title.ext`
47
39
 
48
40
  ```
49
41
  assets/documents/reports/2026-03-01_Annual_Report.pdf
50
- assets/documents/minutes/2026-02-15_Board_Meeting.docx
51
42
  ```
52
43
 
53
- - Date: `YYYY-MM-DD`
54
- - Title: Underscores → spaces
55
- - Category: From folder name
56
-
57
-
58
-
59
- ## File Type Icons
60
-
61
- The plugin automatically displays file type icons for all supported formats. Icons are included in the gem and based on designs from [svgrepo.com](https://www.svgrepo.com/).
44
+ Date `YYYY-MM-DD` • Title → underscores to spaces • Category → folder name
62
45
 
63
- **Icon Sets:** color (default), lines, minimal, ultra-minimal
46
+ ## Icons
64
47
 
65
- **Supported:** PDF, DOCX, XLSX, PPTX, ODT, ODS, ODP, TXT, ZIP, MP3, MP4, JPG, PNG, HTML, XML, CSV, RTF
48
+ 4 icon sets: `color` (default), `lines`, `minimal`, `ultra-minimal`
66
49
 
67
- **Configuration:**
68
50
  ```yaml
69
51
  documents:
70
- icon_set: "color" # color, lines, minimal, ultra-minimal
52
+ icon_set: "color"
71
53
  ```
72
54
 
73
- **Icons appear in:** document pages, lists, search results, category folders
74
-
75
- **Liquid filters:**
76
55
  ```liquid
77
56
  {{ page.file_type | file_type_icon_tag }}
78
- {{ page.file_type | file_type_icon }}
79
57
  ```
80
58
 
81
- *Icons from [svgrepo.com](https://www.svgrepo.com/)*
82
-
83
- ## Includes
59
+ ## Usage
84
60
 
85
61
  ```liquid
86
62
  {% include latest_documents.html count=5 %}
87
63
  {% include documents_list.html %}
88
- {% include category_list.html %}
89
64
  {% include documents_search.html %}
90
65
  ```
91
66
 
92
- ## Search
93
-
94
- ```html
95
- <script src="https://unpkg.com/lunr/lunr.js"></script>
96
- <script src="{{ '/assets/js/documents-search.js' | relative_url }}"></script>
97
- ```
98
67
  ## Configuration
99
68
 
100
69
  ```yaml
101
70
  documents:
102
71
  root: "assets/documents"
103
- permalink: "/documents/:category/:slug/"
104
- icon_set: "color" # color, lines, minimal, ultra-minimal
105
- include_extensions: [".pdf", ".docx", ".pptx", ".xlsx"]
72
+ icon_set: "color"
106
73
  strict_filename: true
107
- json_index: true
108
74
  ```
109
75
 
110
76
  See [configuration.rb](lib/jekyll/documents/configuration.rb) for all options.
77
+
111
78
  ## Development
112
79
 
113
80
  ```bash
114
- bundle install
115
- bundle exec rspec # Run tests
116
- bundle exec rake doc # Generate docs
117
- cd example && bundle exec jekyll serve # Example site
81
+ rake # All quality checks
82
+ rake quick # Fast check
83
+ rake help # Show commands
118
84
  ```
119
85
 
120
- ## License
86
+ **Quality**: 98.99% coverage • 0 vulnerabilities • RuboCop • Reek
87
+
88
+ See [README.Development.md](README.Development.md) for details.
121
89
 
122
- AGPL-3.0-only
90
+ ## Release
123
91
 
124
- ## Attribution
92
+ ```bash
93
+ ./bump_version.sh patch
94
+ ./release.sh
95
+ ```
96
+
97
+ ## License
125
98
 
126
- Icons by [SVG Repo](https://www.svgrepo.com)
99
+ AGPL-3.0-only • Icons by [SVG Repo](https://www.svgrepo.com)
@@ -1,8 +1,10 @@
1
+ {% assign first_doc = site.documents | first %}
2
+ {% assign icon_set = first_doc.icon_set | default: 'color' %}
1
3
  {% assign cats = site.documents | map: "category" | uniq | sort %}
2
4
  <ul class="documents-category-list">
3
5
  {% for cat in cats %}
4
6
  <li>
5
- <img src="/assets/icons/{{ site.documents.icon_set | default: 'color' }}/folder-svgrepo-com.svg" alt="Folder" class="folder-icon" />
7
+ <img src="{{ '/assets/icons/' | append: icon_set | append: '/folder-svgrepo-com.svg' | relative_url }}" alt="Folder" class="folder-icon" />
6
8
  <span class="category-name">{{ cat }}</span>
7
9
  <span class="category-count">({{ site.documents | where: "category", cat | size }})</span>
8
10
  </li>
@@ -2,7 +2,7 @@
2
2
  <ul class="documents-list">
3
3
  {% for doc in docs %}
4
4
  <li>
5
- {{ doc.file_type | file_type_icon_tag }}
5
+ <img src="{{ doc.icon_url | relative_url }}" alt="{{ doc.file_type | upcase }} file" class="file-icon" />
6
6
  <a href="{{ doc.url | relative_url }}">{{ doc.title }}</a>
7
7
  <small>({{ doc.date | date: "%Y-%m-%d" }}, {{ doc.category }})</small>
8
8
  </li>
@@ -2,3 +2,5 @@
2
2
  <input id="doc-search-input" type="search" placeholder="Search documents…" aria-label="Search documents" />
3
3
  <div id="doc-search-results" class="documents-search-results"></div>
4
4
  </div>
5
+ <script src="https://unpkg.com/lunr/lunr.js"></script>
6
+ <script src="{{ '/assets/js/documents-search.js' | relative_url }}"></script>
@@ -1,9 +1,9 @@
1
- {% assign count = include.count | default: site.config.documents.latest_default_count | default: 5 %}
1
+ {% assign count = include.count | default: 5 %}
2
2
  {% assign docs = site.documents | sort: "date" | reverse | slice: 0, count %}
3
3
  <ul class="latest-documents">
4
4
  {% for doc in docs %}
5
5
  <li>
6
- {{ doc.file_type | file_type_icon_tag }}
6
+ <img src="{{ doc.icon_url | relative_url }}" alt="{{ doc.file_type | upcase }} file" class="file-icon" />
7
7
  <a href="{{ doc.url | relative_url }}">{{ doc.title }}</a>
8
8
  <small>({{ doc.date | date: "%Y-%m-%d" }}, {{ doc.category }})</small>
9
9
  </li>
@@ -13,7 +13,7 @@ layout: default
13
13
 
14
14
  <p>
15
15
  <a href="{{ page.file_url | relative_url }}" class="document-download">
16
- {{ page.file_type | file_type_icon_tag }}
16
+ <img src="{{ page.icon_url | relative_url }}" alt="{{ page.file_type | upcase }} file" class="file-icon" />
17
17
  Download ({{ page.extension | remove: "." | upcase }})
18
18
  </a>
19
19
  </p>
@@ -3,25 +3,42 @@
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "jekyll-documents"
5
5
  spec.version = File.read(File.expand_path("lib/jekyll/documents/version.rb", __dir__))
6
- .match(/VERSION\s*=\s*"([^"]+)"/)[1]
6
+ .match(/VERSION\s*=\s*"([^"]+)"/)[1]
7
7
  spec.authors = ["Svend Gundestrup"]
8
8
  spec.email = ["no-reply@borg-collective.eu"]
9
9
 
10
- spec.summary = "Auto-generate Jekyll pages for documents (PDF/DOCX/...) with category/date/title parsing."
11
- spec.description = "A Jekyll plugin + theme that scans assets/documents/**, creates a collection item per file, " \
12
- "adds metadata (title/date/category), outputs pages, generates a JSON index for Lunr, and integrates with jekyll-sitemap."
10
+ spec.summary = "Auto-generate Jekyll pages for documents (PDF/DOCX/...) " \
11
+ "with category/date/title parsing."
12
+ spec.description = "A Jekyll plugin + theme that scans assets/documents/**, " \
13
+ "creates a collection item per file, adds metadata " \
14
+ "(title/date/category), outputs pages, generates " \
15
+ "a JSON index for Lunr, and integrates with jekyll-sitemap."
13
16
  spec.homepage = "https://github.com/gundestrup/jekyll-documents"
14
17
  spec.license = "AGPL-3.0-only"
15
18
 
16
- spec.required_ruby_version = ">= 2.7"
19
+ spec.metadata = {
20
+ "source_code_uri" => "https://github.com/gundestrup/jekyll-documents",
21
+ "bug_tracker_uri" => "https://github.com/gundestrup/jekyll-documents/issues",
22
+ "changelog_uri" => "https://github.com/gundestrup/jekyll-documents/blob/main/CHANGELOG.md",
23
+ "documentation_uri" => "https://github.com/gundestrup/jekyll-documents",
24
+ "homepage_uri" => "https://github.com/gundestrup/jekyll-documents",
25
+ "rubygems_mfa_required" => "true"
26
+ }
27
+
28
+ spec.required_ruby_version = ">= 3.1"
17
29
 
18
30
  spec.files = Dir.glob("{lib,assets,_includes,_layouts}/**/*") +
19
31
  ["README.md", "CHANGELOG.md", "LICENSE", "jekyll-documents.gemspec"]
20
32
  spec.require_paths = ["lib"]
21
33
 
22
34
  spec.add_dependency "jekyll", ">= 4.0"
23
-
24
- spec.add_development_dependency "rspec", "~> 3.12"
35
+
36
+ spec.add_development_dependency "bundler-audit", "~> 0.9"
25
37
  spec.add_development_dependency "rake", "~> 13.0"
38
+ spec.add_development_dependency "reek", "~> 6.1"
39
+ spec.add_development_dependency "rspec", "~> 3.12"
40
+ spec.add_development_dependency "rubocop", "~> 1.60"
41
+ spec.add_development_dependency "rubocop-performance", "~> 1.20"
42
+ spec.add_development_dependency "simplecov", "~> 1.0"
26
43
  spec.add_development_dependency "yard", "~> 0.9"
27
44
  end
@@ -95,6 +95,18 @@ module Jekyll
95
95
  }
96
96
  }.freeze
97
97
 
98
+ # Returns the icon URL for a given file type and explicit icon set
99
+ # @param file_type [String] the file extension (e.g., 'pdf', 'docx')
100
+ # @param icon_set [String] the icon set name (defaults to 'color')
101
+ # @return [String] the icon file URL
102
+ # @example
103
+ # FileTypeIcons.icon_for('pdf', 'lines') #=> "/assets/icons/lines/pdf-svgrepo-com.svg"
104
+ def self.icon_for(file_type, icon_set = "color")
105
+ icons = ICON_MAP[icon_set] || ICON_MAP["color"]
106
+ icons[file_type.to_s.downcase] ||
107
+ "/assets/icons/#{icon_set}/unknown-document-svgrepo-com.svg"
108
+ end
109
+
98
110
  # Returns the icon URL for a given file type using configured icon set
99
111
  # @param file_type [String] the file extension (e.g., 'pdf', 'docx')
100
112
  # @param context [Liquid::Context] the Liquid context for accessing site config
@@ -102,9 +114,7 @@ module Jekyll
102
114
  # @example
103
115
  # file_type_icon('pdf') #=> "/assets/icons/color/pdf.svg"
104
116
  def file_type_icon(file_type, context = nil)
105
- icon_set = get_icon_set(context)
106
- icons = ICON_MAP[icon_set] || ICON_MAP["color"]
107
- icons[file_type.to_s.downcase] || "/assets/icons/#{icon_set}/unknown-document-svgrepo-com.svg"
117
+ Jekyll::Documents::FileTypeIcons.icon_for(file_type, get_icon_set(context))
108
118
  end
109
119
 
110
120
  # Returns an HTML img tag for the file type icon
@@ -114,10 +124,11 @@ module Jekyll
114
124
  # @param context [Liquid::Context] the Liquid context for accessing site config
115
125
  # @return [String] HTML img tag
116
126
  # @example
117
- # file_type_icon_tag('pdf') #=> '<img src="/assets/icons/color/pdf.svg" alt="PDF file" class="file-icon" />'
127
+ # file_type_icon_tag('pdf') #=> \
128
+ # '<img src="/assets/icons/color/pdf.svg" alt="PDF file" class="file-icon" />'
118
129
  def file_type_icon_tag(file_type, css_class: "file-icon", alt: nil, context: nil)
119
130
  url = file_type_icon(file_type, context)
120
- alt_text = alt || "#{file_type.upcase} file"
131
+ alt_text = alt || "#{file_type.to_s.upcase} file"
121
132
  %(<img src="#{url}" alt="#{alt_text}" class="#{css_class}" />)
122
133
  end
123
134
 
@@ -128,13 +139,11 @@ module Jekyll
128
139
  # @return [String] the icon set name
129
140
  def get_icon_set(context)
130
141
  return "color" unless context
131
-
132
- site_config = context.registers[:site]&.config
133
- documents_config = site_config["documents"] if site_config
134
- icon_set = documents_config["icon_set"] if documents_config
135
-
136
- # Validate icon set exists
142
+
143
+ site = context.registers[:site]
144
+ icon_set = site&.config&.dig("documents", "icon_set")
137
145
  return "color" unless icon_set && ICON_MAP.key?(icon_set)
146
+
138
147
  icon_set
139
148
  end
140
149
  end
@@ -9,18 +9,19 @@ module Jekyll
9
9
  }.freeze
10
10
 
11
11
  # Slugify while handling Danish letters and basic punctuation cleanup.
12
- def documents_slugify(input, downcase = true, danish_map = true)
13
- s = input.to_s.dup
14
- s = s.gsub(/[æøåÆØÅ]/) { |ch| DANISH_MAP[ch] } if danish_map
15
- s = s.strip.gsub(/[^\p{Alnum}\-_\s]/u, "")
16
- s = s.tr(" ", "-").gsub(/-+/, "-")
17
- s = s.gsub(/[æøåÆØÅ]/, "") unless danish_map # Remove Danish chars if mapping disabled
18
- s = s.downcase if downcase
19
- s
12
+ def documents_slugify(input, downcase: true, danish_map: true)
13
+ result = input.to_s.dup
14
+ result = result.gsub(/[æøåÆØÅ]/) { |ch| DANISH_MAP[ch] } if danish_map
15
+ result = result.strip.gsub(/[^\p{Alnum}\-_\s]/u, "")
16
+ result = result.tr(" ", "-").squeeze("-")
17
+ result = result.gsub(/[æøåÆØÅ]/, "") unless danish_map
18
+ result = result.downcase if downcase
19
+ result
20
20
  end
21
21
 
22
22
  # Title from filename e.g. "2026-03-01_Title_Here" -> "Title Here"
23
23
  def documents_title_from_filename(basename)
24
+ basename = basename.to_s
24
25
  if basename =~ /^\d{4}-\d{2}-\d{2}_(.+)$/
25
26
  Regexp.last_match(1).tr("_", " ")
26
27
  else
@@ -35,17 +35,19 @@ module Jekyll
35
35
  end
36
36
  next unless @config["include_extensions"].include?(ext)
37
37
 
38
- rel_path = path.start_with?(site.source) ? path[(site.source.length + 1)..-1] : path
38
+ rel_path = path.delete_prefix("#{site.source}/")
39
39
  category = infer_category_from(rel_path)
40
40
  basename = File.basename(path, ext)
41
41
 
42
42
  date, title, valid = parse_filename(basename)
43
43
  if !valid && @config["strict_filename"]
44
44
  ::Jekyll.logger.abort_with "jekyll-documents",
45
- "Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
45
+ "Filename must be 'YYYY-MM-DD_Title.ext' → #{rel_path}"
46
46
  end
47
47
 
48
48
  slug = build_slug(basename)
49
+ file_type = ext.sub(".", "").downcase
50
+ icon_set = @config["icon_set"]
49
51
 
50
52
  doc = ::Jekyll::Document.new(
51
53
  source_stub_for(basename, category),
@@ -53,17 +55,20 @@ module Jekyll
53
55
  collection: collection
54
56
  )
55
57
 
56
- doc.data["layout"] = @config["layout"]
57
- doc.data["title"] = title
58
- doc.data["date"] = date || File.mtime(path)
59
- doc.data["category"] = remap_category(category)
60
- doc.data["file_url"] = "/" + rel_path
61
- doc.data["extension"] = ext
62
- doc.data["file_type"] = ext.sub(".", "").downcase
63
- doc.data["slug"] = slug
64
- doc.data["permalink"] = @config["permalink"]
65
- .gsub(":category", doc.data["category"].to_s)
66
- .gsub(":slug", slug)
58
+ data = doc.data
59
+ data["layout"] = @config["layout"]
60
+ data["title"] = title
61
+ data["date"] = date || File.mtime(path)
62
+ data["category"] = remap_category(category)
63
+ data["file_url"] = "/#{rel_path}"
64
+ data["extension"] = ext
65
+ data["file_type"] = file_type
66
+ data["icon_set"] = icon_set
67
+ data["icon_url"] = Jekyll::Documents::FileTypeIcons.icon_for(file_type, icon_set)
68
+ data["slug"] = slug
69
+ data["permalink"] = @config["permalink"]
70
+ .gsub(":category", data["category"].to_s)
71
+ .gsub(":slug", slug)
67
72
 
68
73
  doc.content = "Auto-generated document page."
69
74
 
@@ -99,7 +104,8 @@ module Jekyll
99
104
  # @return [String] the category name
100
105
  def infer_category_from(rel_path)
101
106
  return "uncategorized" unless @config["categories_from_path"]
102
- category_dir = File.dirname(rel_path).sub("#{@config['root']}", "")
107
+
108
+ category_dir = File.dirname(rel_path).sub(@config["root"].to_s, "")
103
109
  category_dir.split("/").reject(&:empty?).last || "uncategorized"
104
110
  end
105
111
 
@@ -116,7 +122,10 @@ module Jekyll
116
122
  # @return [Array<Date, String, Boolean>] date, title, and validity flag
117
123
  def parse_filename(basename)
118
124
  if basename =~ /^(\d{4})-(\d{2})-(\d{2})_(.+)$/
119
- date = Date.parse("#{Regexp.last_match(1)}-#{Regexp.last_match(2)}-#{Regexp.last_match(3)}")
125
+ year = Regexp.last_match(1).to_i
126
+ month = Regexp.last_match(2).to_i
127
+ day = Regexp.last_match(3).to_i
128
+ date = Date.new(year, month, day)
120
129
  title = Regexp.last_match(4).tr("_", " ")
121
130
  [date, title, true]
122
131
  else
@@ -130,12 +139,16 @@ module Jekyll
130
139
  # @param basename [String] the filename without extension
131
140
  # @return [String] the generated slug
132
141
  def build_slug(basename)
133
- s = basename.sub(/^\d{4}-\d{2}-\d{2}_/, "")
134
- s = s.gsub(/[æøåÆØÅ]/, {"æ"=>"ae","ø"=>"oe","å"=>"aa","Æ"=>"Ae","Ø"=>"Oe","Å"=>"Aa"}) if @config["slug_danish_map"]
135
- s = s.downcase if @config["slug_downcase"]
136
- s = s.gsub(/[^\p{Alnum}\-_\s]/u, "").tr("_ ", "--").gsub(/-+/, "-")
137
- s = s.sub(/^-+/, "").sub(/-+$/, "") # Remove leading/trailing hyphens
138
- s.empty? ? "untitled" : s
142
+ slug = basename.sub(/^\d{4}-\d{2}-\d{2}_/, "")
143
+ if @config["slug_danish_map"]
144
+ slug = slug.gsub(/[æøåÆØÅ]/,
145
+ { "æ" => "ae", "ø" => "oe", "å" => "aa", "Æ" => "Ae", "Ø" => "Oe",
146
+ "Å" => "Aa" })
147
+ end
148
+ slug = slug.downcase if @config["slug_downcase"]
149
+ slug = slug.gsub(/[^\p{Alnum}\-_\s]/u, "").tr("_ ", "--").squeeze("-")
150
+ slug = slug.sub(/^-+/, "").sub(/-+$/, "")
151
+ slug.empty? ? "untitled" : slug
139
152
  end
140
153
  end
141
154
  end
@@ -16,15 +16,16 @@ module Jekyll
16
16
  docs = site.collections["documents"]&.docs || []
17
17
  return if docs.empty?
18
18
 
19
- items = docs.map do |d|
19
+ items = docs.map do |doc|
20
+ data = doc.data
20
21
  {
21
- "url" => d.url,
22
- "title" => d.data["title"],
23
- "category" => d.data["category"],
24
- "date" => (d.data["date"] || Time.at(0)).strftime("%Y-%m-%d"),
25
- "slug" => d.data["slug"],
26
- "file_type" => d.data["file_type"],
27
- "extension" => d.data["extension"]
22
+ "url" => doc.url,
23
+ "title" => data["title"],
24
+ "category" => data["category"],
25
+ "date" => (data["date"] || Time.at(0)).strftime("%Y-%m-%d"),
26
+ "slug" => data["slug"],
27
+ "file_type" => data["file_type"],
28
+ "extension" => data["extension"]
28
29
  }
29
30
  end
30
31
 
@@ -7,7 +7,7 @@ module Jekyll
7
7
  super
8
8
  @args = parse_args(markup)
9
9
  end
10
-
10
+
11
11
  public_class_method :new
12
12
 
13
13
  def render(context)
@@ -17,14 +17,15 @@ module Jekyll
17
17
  category = @args["category"]
18
18
 
19
19
  docs = site.collections["documents"]&.docs || []
20
- docs = docs.select { |d| d.data["category"] == category } if category
21
- docs = docs.sort_by { |d| d.data["date"] || Time.at(0) }.reverse.first(count)
20
+ docs = docs.select { |doc| doc.data["category"] == category } if category
21
+ docs = docs.sort_by { |doc| doc.data["date"] || Time.at(0) }.reverse.first(count)
22
22
 
23
23
  out = +"<ul class=\"latest-documents\">\n"
24
- docs.each do |d|
25
- title = escape_html(d.data["title"])
26
- url = escape_html(d.url)
27
- date = (d.data["date"] || Time.at(0)).strftime("%Y-%m-%d")
24
+ docs.each do |doc|
25
+ data = doc.data
26
+ title = escape_html(data["title"])
27
+ url = escape_html(doc.url)
28
+ date = (data["date"] || Time.at(0)).strftime("%Y-%m-%d")
28
29
  out << %(<li><a href="#{url}">#{title}</a> <small>(#{date})</small></li>\n)
29
30
  end
30
31
  out << "</ul>\n"
@@ -35,23 +36,26 @@ module Jekyll
35
36
 
36
37
  def escape_html(text)
37
38
  return "" unless text
39
+
38
40
  text.to_s.gsub(/[&<>"']/, {
39
- "&" => "&amp;",
40
- "<" => "&lt;",
41
- ">" => "&gt;",
42
- '"' => "&quot;",
43
- "'" => "&#39;"
44
- })
41
+ "&" => "&amp;",
42
+ "<" => "&lt;",
43
+ ">" => "&gt;",
44
+ '"' => "&quot;",
45
+ "'" => "&#39;"
46
+ })
45
47
  end
46
48
 
47
49
  def parse_args(markup)
48
50
  # supports: count:5 category:'referat'
49
51
  args = {}
50
- markup.scan(/(\w+)\s*:\s*'([^']*)'|(\w+)\s*:\s*([^\s]+)/).each do |m|
51
- if m[0]
52
- args[m[0]] = m[1]
52
+ # Safe: markup comes from Jekyll template authors (trusted), not end users
53
+ # Runs only during static site generation, not on user requests
54
+ markup.scan(/(\w+)\s*:\s*'([^']*)'|(\w+)\s*:\s*([^\s]+)/).each do |match|
55
+ if match[0]
56
+ args[match[0]] = match[1]
53
57
  else
54
- args[m[2]] = m[3]
58
+ args[match[2]] = match[3]
55
59
  end
56
60
  end
57
61
  args
@@ -61,4 +65,3 @@ module Jekyll
61
65
  end
62
66
 
63
67
  Liquid::Template.register_tag("latest_documents", Jekyll::Documents::LatestDocumentsTag)
64
-
@@ -4,12 +4,15 @@ module Jekyll
4
4
  module Documents
5
5
  # A tiny StaticFile subclass for writing text files during build.
6
6
  class TextStaticFile < ::Jekyll::StaticFile
7
+ attr_reader :data
8
+
7
9
  def initialize(site, base, rel_dest, content)
8
10
  @site = site
9
11
  @base = base
10
12
  @dir = File.dirname(rel_dest)
11
13
  @name = File.basename(rel_dest)
12
14
  @content = content
15
+ @data = { "sitemap" => false }
13
16
  super(site, base, @dir, @name)
14
17
  end
15
18
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Documents
5
- VERSION = "0.1.1"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-documents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svend Gundestrup
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-07-30 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: jekyll
@@ -24,19 +25,19 @@ dependencies:
24
25
  - !ruby/object:Gem::Version
25
26
  version: '4.0'
26
27
  - !ruby/object:Gem::Dependency
27
- name: rspec
28
+ name: bundler-audit
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - "~>"
31
32
  - !ruby/object:Gem::Version
32
- version: '3.12'
33
+ version: '0.9'
33
34
  type: :development
34
35
  prerelease: false
35
36
  version_requirements: !ruby/object:Gem::Requirement
36
37
  requirements:
37
38
  - - "~>"
38
39
  - !ruby/object:Gem::Version
39
- version: '3.12'
40
+ version: '0.9'
40
41
  - !ruby/object:Gem::Dependency
41
42
  name: rake
42
43
  requirement: !ruby/object:Gem::Requirement
@@ -51,6 +52,76 @@ dependencies:
51
52
  - - "~>"
52
53
  - !ruby/object:Gem::Version
53
54
  version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: reek
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.12'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.60'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.60'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-performance
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.20'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.20'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
54
125
  - !ruby/object:Gem::Dependency
55
126
  name: yard
56
127
  requirement: !ruby/object:Gem::Requirement
@@ -193,7 +264,14 @@ files:
193
264
  homepage: https://github.com/gundestrup/jekyll-documents
194
265
  licenses:
195
266
  - AGPL-3.0-only
196
- metadata: {}
267
+ metadata:
268
+ source_code_uri: https://github.com/gundestrup/jekyll-documents
269
+ bug_tracker_uri: https://github.com/gundestrup/jekyll-documents/issues
270
+ changelog_uri: https://github.com/gundestrup/jekyll-documents/blob/main/CHANGELOG.md
271
+ documentation_uri: https://github.com/gundestrup/jekyll-documents
272
+ homepage_uri: https://github.com/gundestrup/jekyll-documents
273
+ rubygems_mfa_required: 'true'
274
+ post_install_message:
197
275
  rdoc_options: []
198
276
  require_paths:
199
277
  - lib
@@ -201,14 +279,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
201
279
  requirements:
202
280
  - - ">="
203
281
  - !ruby/object:Gem::Version
204
- version: '2.7'
282
+ version: '3.1'
205
283
  required_rubygems_version: !ruby/object:Gem::Requirement
206
284
  requirements:
207
285
  - - ">="
208
286
  - !ruby/object:Gem::Version
209
287
  version: '0'
210
288
  requirements: []
211
- rubygems_version: 4.0.3
289
+ rubygems_version: 3.4.19
290
+ signing_key:
212
291
  specification_version: 4
213
292
  summary: Auto-generate Jekyll pages for documents (PDF/DOCX/...) with category/date/title
214
293
  parsing.