intranet-pandoc 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: