intranet-pandoc 0.0.0 → 1.0.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: 24560ea4df2f1a415458f86f96fa27895ab40d3379644ece2a7a0899555b7581
4
- data.tar.gz: 852e88cd84850b7c5d87c5adb36859da31aaa201cdc149763ba7b2703818281c
3
+ metadata.gz: 2e9ba0d93e05f580d6620fe156202f62fe7a5ed1c4c8f86ecc412204beccff24
4
+ data.tar.gz: dee2cfa108cfd44b4442909185c8c613d277c5ba8115059218b2fb8e2e5ba55b
5
5
  SHA512:
6
- metadata.gz: 7b37d13b54a0cb20801f3722249d51dbc07f7adeb341ef0271164b32c24531c404c835ad4040b4397ff06f6e2ce6f2ca2ccc249747b2b9ac677b7282e0406ae7
7
- data.tar.gz: dc1bbbee393733e9e53fba1bf6973742d00884941aed8c153a25e53c54e3e9c3997170311c90deaac69967615773011ea4c92540672cbe17a8be4a871cc51833
6
+ metadata.gz: 97a49825a5b70100d6243910bb473cf24ad4e3aae58acc95a5deadf3b13125daf8d0e1579b89c9fb6bb287cd44203ff86600d39d56f774797845657c1f3e769a
7
+ data.tar.gz: 661e974bb3aaf1607fee00cc193b8e53ef454965bcbb4bc88362d03c7da5adab00422a39a448d7c20c59c7cf7d6c5ac8c2a1364b898ae341cc1d7a9c2b88104e
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'intranet/abstract_responder'
4
+ require 'intranet/core/haml_wrapper'
5
+ require 'mimemagic'
6
+ require 'pandoc-ruby'
7
+ require_relative 'version'
8
+
9
+ module Intranet
10
+ module Pandoc
11
+ # The responder for the Pandoc module of the Intranet.
12
+ class Responder < AbstractResponder
13
+ include Core::HamlWrapper # 'inherits' from methods of HamlWrapper
14
+
15
+ # Returns the name of the module.
16
+ # @return [String] The name of the module.
17
+ def self.module_name
18
+ NAME
19
+ end
20
+
21
+ # The version of the module, according to semantic versionning.
22
+ # @return [String] The version of the module.
23
+ def self.module_version
24
+ VERSION
25
+ end
26
+
27
+ # The homepage of the module.
28
+ # @return [String] The homepage URL of the module.
29
+ def self.module_homepage
30
+ HOMEPAGE_URL
31
+ end
32
+
33
+ # Initializes a new Pandoc responder instance.
34
+ # @param title [String] The title of the module instance.
35
+ # @param rootdir [String] The root directory of the module instance.
36
+ # @param in_menu [Boolean] Whether the module instance should be displayed in the main
37
+ # navigation menu or not.
38
+ def initialize(logger, title, rootdir, template = nil, in_menu = true)
39
+ @logger = logger
40
+ @title = title
41
+ @rootdir = rootdir
42
+ @template = template
43
+ @in_menu = in_menu
44
+ end
45
+
46
+ # Specifies if the responder instance should be displayed in the main navigation menu or not.
47
+ # @return [Boolean] True if the responder instance should be added to the main navigation
48
+ # menu, False otherwise.
49
+ def in_menu?
50
+ @in_menu
51
+ end
52
+
53
+ # Specifies the absolute path to the resources directory for that module.
54
+ # @return [String] The absolute path to the resources directory for the module.
55
+ def resources_dir
56
+ File.absolute_path(File.join('..', 'resources'), __dir__)
57
+ end
58
+
59
+ # Generates the HTML content associated to the given +path+ and +query+.
60
+ # @param path [String] The requested URI, relative to that module root URI.
61
+ # @param query [Hash<String,String>] The URI variable/value pairs, if any.
62
+ # @return [Integer, String, String] The HTTP return code, the MIME type and the answer body.
63
+ def generate_page(path, query)
64
+ case path
65
+ when %r{^/.+\.html$} then serve_page(path.gsub(%r{^/(.+)\.html$}, '\\1'))
66
+ when %r{^/.+\.(jpg|png)$} then serve_media(path.gsub(%r{^/(.+)$}, '\\1'))
67
+ else super(path, query)
68
+ end
69
+ end
70
+
71
+ # The title of the module instance, as displayed on the web page.
72
+ # @return [String] The title of the module instance.
73
+ attr_reader :title
74
+
75
+ private
76
+
77
+ # Provides the list of required stylesheets.
78
+ # @return [Array<String>] The list of required stylesheets.
79
+ def stylesheets
80
+ ['design/style.css']
81
+ end
82
+
83
+ # Provides the list of required script files.
84
+ # @return [Array<Hash<Symbol,String>>] The list of required scripts.
85
+ def scripts
86
+ []
87
+ end
88
+
89
+ # Provides the list of Lua filters to be passed to Pandoc when converting mardown to HTML.
90
+ # @return [Array<String>] The list of Lua filters, relative to the .
91
+ def lua_filters
92
+ [
93
+ File.join(resources_dir, 'filters', 'titles-shift-levels.lua'),
94
+ File.join(resources_dir, 'filters', 'links-open-external-in-new-tab.lua')
95
+ ]
96
+ end
97
+
98
+ ##########################################################################
99
+ ### Servicing of the HTML "display-able" content ###
100
+ ##########################################################################
101
+
102
+ # Convert a single markdown file to HTML format and returns the markup.
103
+ # @param path [String] The path to the markdown file, relative to the module root directory
104
+ # and without extension.
105
+ # @raise [RuntimeError] If pandoc fails for whatever reason.
106
+ def convert_to_html(path)
107
+ input = "\"#{File.join(@rootdir, "#{path}.md")}\""
108
+ filters = lua_filters.map { |filter| "--lua-filter=\"#{filter}\"" }
109
+ options = ['--standalone', "--template=\"#{@template}\""] unless @template.to_s.empty?
110
+ PandocRuby.new([input], filters, options).to_html
111
+ end
112
+
113
+ def serve_page(path)
114
+ content = "<section>#{convert_to_html(path)}</section>"
115
+ [206, 'text/html',
116
+ { content: content, title: @title, stylesheets: stylesheets, scripts: scripts }]
117
+ rescue RuntimeError => e
118
+ @logger.debug(e.message.chomp)
119
+ [404, '', '']
120
+ end
121
+
122
+ ##########################################################################
123
+ ### Servicing of the media (pictures) ###
124
+ ##########################################################################
125
+
126
+ def serve_media(path)
127
+ media_path = File.join(@rootdir, path)
128
+ [200, MimeMagic.by_path(media_path).type, File.read(media_path)]
129
+ rescue Errno::ENOENT
130
+ [404, '', '']
131
+ end
132
+ end
133
+ end
134
+ end
@@ -8,7 +8,7 @@ module Intranet
8
8
  NAME = 'intranet-pandoc'
9
9
 
10
10
  # The version of the gem, according to semantic versionning.
11
- VERSION = '0.0.0'
11
+ VERSION = '1.0.0'
12
12
 
13
13
  # The URL of the gem homepage.
14
14
  HOMEPAGE_URL = 'https://rubygems.org/gems/intranet-pandoc'
@@ -0,0 +1,10 @@
1
+ -- Lua filter for Pandoc.
2
+
3
+ -- Add target="_blank" attribute to all external links.
4
+ function Link(el)
5
+ if string.match(el.target, '^http') then
6
+ el.attributes.target = '_blank'
7
+ end
8
+ return el
9
+ end
10
+
@@ -0,0 +1,8 @@
1
+ -- Lua filter for Pandoc.
2
+
3
+ -- Shift title levels by one (first-level become h2, second-level become h3,
4
+ -- on so forth). This is to leave h1 for the main page title.
5
+ function Header(el)
6
+ return pandoc.Header(el.level + 1, el.content, el.attr)
7
+ end
8
+
@@ -0,0 +1,21 @@
1
+ /**
2
+ * pandoc/style.css
3
+ * Design for the Pandoc module of the IntraNet.
4
+ */
5
+
6
+ figure {
7
+ max-width: 75%;
8
+ margin: 3em auto; /* top/bottom sides */
9
+ text-align: center;
10
+ }
11
+
12
+ figure > img, p img {
13
+ max-width: 100%;
14
+ }
15
+
16
+ /* Mobile devices only */
17
+ @media only screen and (max-width: 600px), only screen and (max-device-width: 600px) {
18
+ figure {
19
+ max-width: 85%;
20
+ }
21
+ }
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'intranet/core'
4
+ require 'intranet/logger'
5
+ require 'intranet/abstract_responder'
6
+ require 'intranet/pandoc/responder'
7
+
8
+ RSpec.describe Intranet::Pandoc::Responder do
9
+ it 'should inherit from Intranet::AbstractResponder' do
10
+ expect(described_class.superclass).to eql(Intranet::AbstractResponder)
11
+ end
12
+
13
+ it 'should define its name, version and homepage' do
14
+ expect { described_class.module_name }.not_to raise_error
15
+ expect { described_class.module_version }.not_to raise_error
16
+ expect { described_class.module_homepage }.not_to raise_error
17
+ end
18
+
19
+ before do
20
+ logger = Intranet::Logger.new(Intranet::Logger::FATAL)
21
+ @core = Intranet::Core.new(logger)
22
+
23
+ testroot = File.absolute_path('testroot', __dir__)
24
+ @template = File.join(testroot, 'template.html')
25
+ @responder = described_class.new(logger, 'TITLE', testroot, nil)
26
+ @core.register_module(
27
+ @responder, ['pandoc'], File.absolute_path('../../../lib/intranet/resources', __dir__)
28
+ )
29
+ end
30
+
31
+ describe '#in_menu?' do
32
+ it 'should return the value provided at initialization' do
33
+ expect(described_class.new(nil, '', nil, nil, false).in_menu?).to be false
34
+ expect(described_class.new(nil, '', nil, nil, true).in_menu?).to be true
35
+ end
36
+ end
37
+
38
+ describe '#resources_dir' do
39
+ it 'should return the absolute path of the resources directory' do
40
+ expect(described_class.new(nil, '', nil, false).resources_dir).to eql(
41
+ File.absolute_path('../../../lib/intranet/resources', __dir__)
42
+ )
43
+ end
44
+ end
45
+
46
+ describe '#title' do
47
+ it 'should return the title of the webpage provided by the module' do
48
+ expect(@responder.title).to eql('TITLE')
49
+ end
50
+ end
51
+
52
+ describe '#generate_page' do
53
+ context 'when asked for an existing HTML page' do
54
+ it 'should return a partial HTML content generated from the index.md file' do
55
+ code, mime, content = @responder.generate_page('/index.html', {})
56
+ expect(code).to eql(206)
57
+ expect(mime).to eql('text/html')
58
+ expect(content).to eql(
59
+ {
60
+ content: '<section>' \
61
+ "<p>This is index.md, the main markdown page.</p>\n" \
62
+ "<p>We may use the following text attributes:</p>\n" \
63
+ "<ul>\n" \
64
+ "<li><em>italic</em></li>\n" \
65
+ "<li><strong>bold</strong></li>\n" \
66
+ "<li><code>monospace</code></li>\n" \
67
+ "</ul>\n" \
68
+ "<p>We may produce an horizontal rule:</p>\n" \
69
+ "<hr />\n" \
70
+ "<p>Two spaces at the end of a line<br />\nproduce a line break.</p>\n" \
71
+ "<blockquote>\n" \
72
+ "<p>Markdown uses email-style characters for blockquoting.</p>\n" \
73
+ "<p>Multiple paragraphs need to be prepended individually.</p>\n" \
74
+ "</blockquote>\n" \
75
+ '</section>',
76
+ title: 'TITLE',
77
+ stylesheets: ['design/style.css'],
78
+ scripts: []
79
+ }
80
+ )
81
+ end
82
+
83
+ it 'should shift titles by one level to correctly fit in intranet page template' do
84
+ code, mime, content = @responder.generate_page('/filters/test-titles-shift-levels.html', {})
85
+ expect(code).to eql(206)
86
+ expect(mime).to eql('text/html')
87
+ expect(content).to eql(
88
+ {
89
+ content: '<section>' \
90
+ "<h2 id=\"level-1-heading\">Level-1 Heading</h2>\n" \
91
+ "<h3 id=\"level-2-heading\">Level-2 Heading</h3>\n" \
92
+ "<h2 id=\"alternative-level-1-heading\">Alternative Level-1 heading</h2>\n" \
93
+ "<h3 id=\"alternative-level-2-heading\">Alternative Level-2 heading</h3>\n" \
94
+ "<h4 id=\"level-3-heading\">Level-3 heading</h4>\n" \
95
+ "<p>Other heading levels are not supported by pandoc markdown.</p>\n" \
96
+ '</section>',
97
+ title: 'TITLE',
98
+ stylesheets: ['design/style.css'],
99
+ scripts: []
100
+ }
101
+ )
102
+ end
103
+
104
+ it 'should add the _blank attributes to external links' do
105
+ code, mime, content = @responder.generate_page('/filters/test-links-open-external-in-new-tab.html', {})
106
+ expect(code).to eql(206)
107
+ expect(mime).to eql('text/html')
108
+ expect(content).to eql(
109
+ {
110
+ content: '<section>' \
111
+ "<p>We use a filter that may produce:</p>\n" \
112
+ "<ul>\n" \
113
+ "<li><a href=\"index.html\">relative internal links</a></li>\n" \
114
+ "<li><a href=\"/index.html\">absolute internal links</a></li>\n" \
115
+ "<li><a href=\"http://example.com\" target=\"_blank\">HTTP external links</a></li>\n" \
116
+ "<li><a href=\"https://example.com\" target=\"_blank\">HTTPS external links</a></li>\n" \
117
+ "</ul>\n" \
118
+ '</section>',
119
+ title: 'TITLE',
120
+ stylesheets: ['design/style.css'],
121
+ scripts: []
122
+ }
123
+ )
124
+ end
125
+
126
+ it 'should use the specified template, if any, to generate the partial HTML content' do
127
+ @responder.instance_variable_set(:@template, @template)
128
+ code, mime, content = @responder.generate_page('/index.html', {})
129
+ expect(code).to eql(206)
130
+ expect(mime).to eql('text/html')
131
+ expect(content).to eql(
132
+ {
133
+ content: '<section>' \
134
+ "<p>This page was generated using <code>template.html</code>.</p>\n" \
135
+ "\n" \
136
+ "<p>This is index.md, the main markdown page.</p>\n" \
137
+ "<p>We may use the following text attributes:</p>\n" \
138
+ "<ul>\n" \
139
+ "<li><em>italic</em></li>\n" \
140
+ "<li><strong>bold</strong></li>\n" \
141
+ "<li><code>monospace</code></li>\n" \
142
+ "</ul>\n" \
143
+ "<p>We may produce an horizontal rule:</p>\n" \
144
+ "<hr />\n" \
145
+ "<p>Two spaces at the end of a line<br />\nproduce a line break.</p>\n" \
146
+ "<blockquote>\n" \
147
+ "<p>Markdown uses email-style characters for blockquoting.</p>\n" \
148
+ "<p>Multiple paragraphs need to be prepended individually.</p>\n" \
149
+ "</blockquote>\n" \
150
+ "\n" \
151
+ "<p>End of template</p>\n" \
152
+ '</section>',
153
+ title: 'TITLE',
154
+ stylesheets: ['design/style.css'],
155
+ scripts: []
156
+ }
157
+ )
158
+ end
159
+ end
160
+
161
+ context 'when asked for an existing picture' do
162
+ it 'should return the picture' do
163
+ # JPG file
164
+ code, mime, content = @responder.generate_page('/white.jpg', {})
165
+ expect(code).to eql(200)
166
+ expect(mime).to eql('image/jpeg')
167
+ expect(content).to eql(File.read(File.join(__dir__, 'testroot', 'white.jpg')))
168
+
169
+ # PNG file
170
+ code, mime, content = @responder.generate_page('/alpha.png', {})
171
+ expect(code).to eql(200)
172
+ expect(mime).to eql('image/png')
173
+ expect(content).to eql(File.read(File.join(__dir__, 'testroot', 'alpha.png')))
174
+ end
175
+ end
176
+
177
+ context 'otherwise' do
178
+ it 'should return an HTTP 404 error' do
179
+ expect(@responder.generate_page('/filters/index.html', {})).to eql([404, '', ''])
180
+ expect(@responder.generate_page('index.html', {})).to eql([404, '', ''])
181
+ expect(@responder.generate_page('/black.jpg', {})).to eql([404, '', ''])
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,6 @@
1
+ We use a filter that may produce:
2
+
3
+ * [relative internal links](index.html)
4
+ * [absolute internal links](/index.html)
5
+ * [HTTP external links](http://example.com)
6
+ * [HTTPS external links](https://example.com)
@@ -0,0 +1,13 @@
1
+ Level-1 Heading
2
+ ===============
3
+
4
+ Level-2 Heading
5
+ ---------------
6
+
7
+ # Alternative Level-1 heading
8
+
9
+ ## Alternative Level-2 heading
10
+
11
+ ### Level-3 heading
12
+
13
+ Other heading levels are not supported by pandoc markdown.
@@ -0,0 +1,19 @@
1
+ This is index.md, the main markdown page.
2
+
3
+ We may use the following text attributes:
4
+
5
+ * _italic_
6
+ * **bold**
7
+ * `monospace`
8
+
9
+ We may produce an horizontal rule:
10
+
11
+ ---
12
+
13
+ Two spaces at the end of a line
14
+ produce a line break.
15
+
16
+ > Markdown uses email-style
17
+ characters for blockquoting.
18
+ >
19
+ > Multiple paragraphs need to be prepended individually.
@@ -0,0 +1,5 @@
1
+ <p>This page was generated using <code>template.html</code>.</p>
2
+
3
+ $body$
4
+
5
+ <p>End of template</p>
data/spec/spec_helper.rb CHANGED
@@ -41,13 +41,11 @@ RSpec.configure do |config|
41
41
  $LOAD_PATH << File.absolute_path(File.join('..', 'lib'), __dir__)
42
42
 
43
43
  # Load and start SimpleCov to gather code coverage information
44
- unless config.files_to_run.one?
45
- require 'simplecov'
46
- SimpleCov.start do
47
- enable_coverage :branch # measure branches coverage
48
- primary_coverage :branch
49
- minimum_coverage line: 100, branch: 100 # minimal coverage rate to succeed
50
- add_filter 'spec' # exclude 'spec' folder from coverage
51
- end
44
+ require 'simplecov'
45
+ SimpleCov.start do
46
+ enable_coverage :branch # measure branches coverage
47
+ primary_coverage :branch
48
+ minimum_coverage line: 100, branch: 100 # minimal coverage rate to succeed
49
+ add_filter 'spec' # exclude 'spec' folder from coverage
52
50
  end
53
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intranet-pandoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ebling Mis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-24 00:00:00.000000000 Z
11
+ date: 2022-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: intranet-core
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '2.0'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 2.4.0
22
+ version: 2.4.5
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,41 @@ dependencies:
29
29
  version: '2.0'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 2.4.0
32
+ version: 2.4.5
33
+ - !ruby/object:Gem::Dependency
34
+ name: mimemagic
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.4'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.4'
47
+ - !ruby/object:Gem::Dependency
48
+ name: pandoc-ruby
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.1.0
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '2.0'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.1.0
33
67
  - !ruby/object:Gem::Dependency
34
68
  name: rake
35
69
  requirement: !ruby/object:Gem::Requirement
@@ -106,7 +140,18 @@ executables: []
106
140
  extensions: []
107
141
  extra_rdoc_files: []
108
142
  files:
143
+ - lib/intranet/pandoc/responder.rb
109
144
  - lib/intranet/pandoc/version.rb
145
+ - lib/intranet/resources/filters/links-open-external-in-new-tab.lua
146
+ - lib/intranet/resources/filters/titles-shift-levels.lua
147
+ - lib/intranet/resources/www/style.css
148
+ - spec/intranet/pandoc/responder_spec.rb
149
+ - spec/intranet/pandoc/testroot/alpha.png
150
+ - spec/intranet/pandoc/testroot/filters/test-links-open-external-in-new-tab.md
151
+ - spec/intranet/pandoc/testroot/filters/test-titles-shift-levels.md
152
+ - spec/intranet/pandoc/testroot/index.md
153
+ - spec/intranet/pandoc/testroot/template.html
154
+ - spec/intranet/pandoc/testroot/white.jpg
110
155
  - spec/spec_helper.rb
111
156
  homepage: https://rubygems.org/gems/intranet-pandoc
112
157
  licenses: