neruda 0.0.9 → 0.2.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 +5 -5
- data/bin/pablo +135 -238
- data/lib/neruda/config.rb +137 -0
- data/lib/neruda/config/lisp_config.rb +254 -0
- data/lib/neruda/config/org-config.el +18 -0
- data/lib/neruda/config/ox-neruda.el +114 -0
- data/lib/neruda/emacs.rb +44 -0
- data/lib/neruda/index.rb +122 -0
- data/lib/neruda/index/atom_generator.rb +86 -0
- data/lib/neruda/index/org_generator.rb +115 -0
- data/lib/neruda/org_file.rb +299 -0
- data/lib/neruda/org_file/class_methods.rb +72 -0
- data/lib/neruda/org_file/extracter.rb +72 -0
- data/lib/neruda/org_file/htmlizer.rb +53 -0
- data/lib/neruda/preview.rb +55 -0
- data/lib/neruda/templater.rb +112 -0
- data/lib/neruda/utils.rb +212 -0
- data/lib/neruda/version.rb +6 -0
- data/lib/tasks/org.rake +84 -0
- data/lib/tasks/site.rake +86 -0
- data/lib/tasks/sync.rake +34 -0
- data/lib/tasks/tags.rake +19 -0
- data/locales/en.yml +37 -0
- data/locales/fr.yml +37 -0
- data/themes/default/css/htmlize.css +346 -0
- data/themes/default/css/style.css +153 -0
- data/themes/default/img/bottom.png +0 -0
- data/themes/default/img/tic.png +0 -0
- data/themes/default/img/top.png +0 -0
- metadata +153 -43
- data/README.md +0 -98
- data/docs/Rakefile.example +0 -4
- data/docs/config.yml.example +0 -17
- data/lib/assets/chapter.slim +0 -14
- data/lib/assets/index.slim +0 -13
- data/lib/assets/layout.slim +0 -17
- data/lib/assets/style.css +0 -199
- data/lib/neruda.rb +0 -106
- data/lib/neruda/chapter.rb +0 -26
- data/lib/neruda/url.rb +0 -14
- data/lib/tasks/book.rake +0 -60
- data/lib/tasks/capistrano/chapters.rake +0 -60
- data/lib/tasks/capistrano/sinatra.rake +0 -18
- data/lib/tasks/chapters.rake +0 -132
- data/lib/tasks/sinatra.rake +0 -36
@@ -0,0 +1,254 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open-uri'
|
4
|
+
require 'neruda/version'
|
5
|
+
|
6
|
+
module Neruda
|
7
|
+
# This module contains utilitary methods to ease ~org-config.el~
|
8
|
+
# file generation
|
9
|
+
module LispConfig
|
10
|
+
# Fetch and return the last published version of Org.
|
11
|
+
#
|
12
|
+
# @return [String] the new x.x.x version string of Org
|
13
|
+
def org_last_version
|
14
|
+
return @org_version if @org_version
|
15
|
+
if File.exist?('tmp/__last_org_version__')
|
16
|
+
@org_version = IO.read('tmp/__last_org_version__')
|
17
|
+
return @org_version
|
18
|
+
end
|
19
|
+
index = URI('https://orgmode.org/index.html').open.read
|
20
|
+
last_ver = index.match(/https:\/\/orgmode\.org\/org-([0-9.]+)\.tar\.gz/)
|
21
|
+
# :nocov:
|
22
|
+
if last_ver.nil?
|
23
|
+
warn 'Org last version not found'
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
FileUtils.mkdir_p 'tmp'
|
27
|
+
IO.write('tmp/__last_org_version__', last_ver[1])
|
28
|
+
# :nocov:
|
29
|
+
@org_version = last_ver[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generate emacs lisp configuration file for Org and write it.
|
33
|
+
#
|
34
|
+
# This method saves the generated configuration in the file
|
35
|
+
# ~org-config.el~ at the root of your project, overwriting it if it
|
36
|
+
# existed already.
|
37
|
+
#
|
38
|
+
# @return [Integer] the length written (as returned by the
|
39
|
+
# underlying ~IO.write~ method call)
|
40
|
+
def write_org_lisp_config(with_tags: false)
|
41
|
+
projects = org_generate_projects(with_tags: with_tags)
|
42
|
+
workdir = Dir.pwd
|
43
|
+
content = IO.read(File.expand_path('./org-config.el', __dir__))
|
44
|
+
.gsub('__VERSION__', Neruda::VERSION)
|
45
|
+
.gsub('__WORK_DIR__', workdir)
|
46
|
+
.gsub('__NERUDA_DIR__', __dir__)
|
47
|
+
.gsub('__ORG_VER__', org_last_version)
|
48
|
+
.gsub('__ALL_PROJECTS__', all_projects(projects))
|
49
|
+
.gsub('__THEME_CONFIG__', org_default_theme_config)
|
50
|
+
.gsub('__ALL_PROJECTS_NAMES__', project_names(projects))
|
51
|
+
.gsub('__LONG_DATE_FMT__', r18n_full_datetime_format)
|
52
|
+
.gsub('__AUTHOR_EMAIL__', settings['author_email'] || '')
|
53
|
+
.gsub('__AUTHOR_NAME__', settings['author'])
|
54
|
+
IO.write("#{workdir}/org-config.el", content)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Generate emacs directory variables file.
|
58
|
+
#
|
59
|
+
# This method generate the file ~.dir-locals.el~, which is
|
60
|
+
# responsible to load neruda Org settings when visiting an Org file
|
61
|
+
# of this neruda instance.
|
62
|
+
#
|
63
|
+
# @return [Integer] the length written (as returned by the
|
64
|
+
# underlying ~IO.write~ method call)
|
65
|
+
def write_dir_locals
|
66
|
+
workdir = Dir.pwd
|
67
|
+
IO.write(
|
68
|
+
"#{workdir}/.dir-locals.el",
|
69
|
+
"((org-mode . ((eval . (load-file \"#{workdir}/org-config.el\")))))"
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def r18n_full_datetime_format
|
76
|
+
locale = R18n.get.locale
|
77
|
+
date_fmt = R18n.t.neruda.index.full_date_format(
|
78
|
+
date: locale.full_format
|
79
|
+
)
|
80
|
+
date_fmt = locale.year_format.sub('_', date_fmt)
|
81
|
+
time_fmt = locale.time_format.delete('_').strip
|
82
|
+
R18n.t.neruda.index.full_date_with_time_format(
|
83
|
+
date: date_fmt, time: time_fmt
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def ruby_to_lisp_boolean(value)
|
88
|
+
return 't' if value == true
|
89
|
+
'nil'
|
90
|
+
end
|
91
|
+
|
92
|
+
def project_names(projects)
|
93
|
+
names = projects.keys.map do |p|
|
94
|
+
["\"#{p}\"", "\"#{p}-assets\""]
|
95
|
+
end.flatten
|
96
|
+
names << "\"theme-#{settings['theme']}\""
|
97
|
+
sources.each do |s|
|
98
|
+
next unless s['theme'] && s['theme'] != settings['theme']
|
99
|
+
theme = "\"theme-#{s['theme']}\""
|
100
|
+
next if names.include? theme
|
101
|
+
names << theme
|
102
|
+
end
|
103
|
+
names.join(' ')
|
104
|
+
end
|
105
|
+
|
106
|
+
def all_projects(projects)
|
107
|
+
projects.values.join("\n").strip
|
108
|
+
.gsub(/\n\s*\n/, "\n")
|
109
|
+
.gsub(/\n/, "\n ")
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return the full path to the publication path of a given project
|
113
|
+
# configuration.
|
114
|
+
#
|
115
|
+
# @param project [Hash] a project configuration (as extracted from
|
116
|
+
# the ~sources~ key)
|
117
|
+
# @return [String] the full path to the target dir of this project
|
118
|
+
def publication_path(project)
|
119
|
+
publish_in = [Dir.pwd, settings['public_folder']]
|
120
|
+
publish_in << project['target'] unless project['target'] == '.'
|
121
|
+
publish_in.join('/')
|
122
|
+
end
|
123
|
+
|
124
|
+
def org_project(project_name, opts)
|
125
|
+
publish_in = publication_path(opts)
|
126
|
+
other_lines = [
|
127
|
+
format(':recursive %<value>s',
|
128
|
+
value: ruby_to_lisp_boolean(opts['recursive']))
|
129
|
+
]
|
130
|
+
if opts['exclude']
|
131
|
+
other_lines << format(':exclude "%<value>s"',
|
132
|
+
value: opts['exclude'])
|
133
|
+
end
|
134
|
+
themeconf = org_theme_config(opts['theme']) || ''
|
135
|
+
<<~ORGPROJECT
|
136
|
+
("#{project_name}"
|
137
|
+
:base-directory "#{opts['path']}"
|
138
|
+
:base-extension "org"
|
139
|
+
#{other_lines.join("\n ")}
|
140
|
+
:publishing-directory "#{publish_in}"
|
141
|
+
:publishing-function org-html-publish-to-html
|
142
|
+
:section-numbers nil
|
143
|
+
:with-toc nil
|
144
|
+
#{opts['org_headers']})
|
145
|
+
("#{project_name}-assets"
|
146
|
+
:base-directory "#{opts['path']}"
|
147
|
+
:base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|svg\\\\\\|pdf"
|
148
|
+
#{other_lines[0]}
|
149
|
+
:publishing-directory "#{publish_in}"
|
150
|
+
:publishing-function org-publish-attachment)
|
151
|
+
#{themeconf}
|
152
|
+
ORGPROJECT
|
153
|
+
end
|
154
|
+
|
155
|
+
def org_default_postamble
|
156
|
+
<<~POSTAMBLE
|
157
|
+
<p><span class="author">#{R18n.t.neruda.org.postamble.written_by}</span>
|
158
|
+
#{R18n.t.neruda.org.postamble.with_emacs}</p>
|
159
|
+
<p class="date">#{R18n.t.neruda.org.postamble.last_modification}</p>
|
160
|
+
<p class="validation">%v</p>
|
161
|
+
POSTAMBLE
|
162
|
+
end
|
163
|
+
|
164
|
+
def org_default_html_head
|
165
|
+
<<~HTMLHEAD
|
166
|
+
<link rel="stylesheet" type="text/css" media="screen"
|
167
|
+
href="__DOMAIN__/assets/__THEME__/css/style.css">
|
168
|
+
<link rel="stylesheet" type="text/css" media="screen"
|
169
|
+
href="__DOMAIN__/assets/__THEME__/css/htmlize.css">
|
170
|
+
__ATOM_FEED__
|
171
|
+
HTMLHEAD
|
172
|
+
end
|
173
|
+
|
174
|
+
def org_default_html_options
|
175
|
+
{ 'html-head' => org_default_html_head,
|
176
|
+
'html-postamble' => org_default_postamble,
|
177
|
+
'html-head-include-default-style' => 't',
|
178
|
+
'html-head-include-scripts' => 'nil' }
|
179
|
+
end
|
180
|
+
|
181
|
+
def expand_vars_in_html_head(head, project)
|
182
|
+
curtheme = project['theme'] || settings['theme']
|
183
|
+
# Head may be frozen when coming from settings
|
184
|
+
head = head.gsub('__THEME__', curtheme)
|
185
|
+
.gsub('__DOMAIN__', settings['domain'])
|
186
|
+
return head.gsub('__ATOM_FEED__', '') unless project['is_blog']
|
187
|
+
atomfeed = <<~ATOMFEED
|
188
|
+
<link rel="alternate" type="application/atom+xml" title="Atom 1.0"
|
189
|
+
href="#{settings['domain']}/feeds/index.xml" />
|
190
|
+
ATOMFEED
|
191
|
+
head.gsub('__ATOM_FEED__', atomfeed)
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_project_org_headers(project)
|
195
|
+
orgtplopts = org_default_html_options.merge(
|
196
|
+
settings['org-html'] || {}, project['org-html'] || {}
|
197
|
+
)
|
198
|
+
orgtpl = []
|
199
|
+
orgtplopts.each do |k, v|
|
200
|
+
v = expand_vars_in_html_head(v, project) if k == 'html-head'
|
201
|
+
val = v.strip.gsub(/"/, '\"')
|
202
|
+
if ['t', 'nil', '1'].include? val
|
203
|
+
orgtpl << ":#{k} #{val}"
|
204
|
+
else
|
205
|
+
orgtpl << ":#{k} \"#{val}\""
|
206
|
+
end
|
207
|
+
end
|
208
|
+
orgtpl.join("\n ")
|
209
|
+
end
|
210
|
+
|
211
|
+
def org_generate_projects(with_tags: false)
|
212
|
+
projects = {}
|
213
|
+
projects_sources = sources
|
214
|
+
if with_tags
|
215
|
+
tags_conf = build_source('tags')
|
216
|
+
tags_conf['recursive'] = false
|
217
|
+
projects_sources << tags_conf
|
218
|
+
end
|
219
|
+
projects_sources.each do |opts|
|
220
|
+
opts['org_headers'] = build_project_org_headers(opts)
|
221
|
+
projects[opts['name']] = org_project(opts['name'], opts)
|
222
|
+
end
|
223
|
+
projects
|
224
|
+
end
|
225
|
+
|
226
|
+
def org_default_theme_config
|
227
|
+
org_theme_config(settings['theme']).split("\n").map do |line|
|
228
|
+
if line[0] == '('
|
229
|
+
line
|
230
|
+
else
|
231
|
+
" #{line}"
|
232
|
+
end
|
233
|
+
end.join("\n")
|
234
|
+
end
|
235
|
+
|
236
|
+
def org_theme_config(theme)
|
237
|
+
return nil if theme.nil?
|
238
|
+
workdir = Dir.pwd
|
239
|
+
if theme == 'default'
|
240
|
+
sourcedir = File.expand_path('../../../', __dir__)
|
241
|
+
else
|
242
|
+
sourcedir = workdir
|
243
|
+
end
|
244
|
+
<<~THEMECONFIG
|
245
|
+
("theme-#{theme}"
|
246
|
+
:base-directory "#{sourcedir}/themes/#{theme}"
|
247
|
+
:base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|js\\\\\\|css\\\\\\|otf\\\\\\|ttf\\\\\\|woff2?"
|
248
|
+
:recursive t
|
249
|
+
:publishing-directory "#{workdir}/#{settings['public_folder']}/assets/#{theme}"
|
250
|
+
:publishing-function org-publish-attachment)
|
251
|
+
THEMECONFIG
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
;; Add org-mode to load path
|
2
|
+
(add-to-list 'load-path (expand-file-name "org-__ORG_VER__/lisp" "__WORK_DIR__"))
|
3
|
+
;; Load last version of htmlize.el
|
4
|
+
(load-file (expand-file-name "htmlize.el" "__WORK_DIR__"))
|
5
|
+
|
6
|
+
;; Current project options
|
7
|
+
(setq neruda/version "__VERSION__"
|
8
|
+
neruda/current-work-dir "__WORK_DIR__"
|
9
|
+
user-mail-address "__AUTHOR_EMAIL__"
|
10
|
+
user-full-name "__AUTHOR_NAME__"
|
11
|
+
org-html-metadata-timestamp-format "__LONG_DATE_FMT__"
|
12
|
+
org-publish-project-alist
|
13
|
+
`(__ALL_PROJECTS__
|
14
|
+
__THEME_CONFIG__
|
15
|
+
("website" :components (__ALL_PROJECTS_NAMES__))))
|
16
|
+
|
17
|
+
;; Load neruda lib
|
18
|
+
(load-file (expand-file-name "ox-neruda.el" "__NERUDA_DIR__"))
|
@@ -0,0 +1,114 @@
|
|
1
|
+
;;; ox-neruda.el --- Neruda Gem specific helpers for Org Export Engine -*- lexical-binding: t; -*-
|
2
|
+
|
3
|
+
;; Copyright (C) 2011-2019 Free Software Foundation, Inc.
|
4
|
+
|
5
|
+
;; Author: Étienne Deparis <etienne at depar dot is>
|
6
|
+
;; Keywords: org, export
|
7
|
+
|
8
|
+
;; This file is not part of GNU Emacs.
|
9
|
+
|
10
|
+
;; GNU Emacs is free software: you can redistribute it and/or modify
|
11
|
+
;; it under the terms of the GNU General Public License as published by
|
12
|
+
;; the Free Software Foundation, either version 3 of the License, or
|
13
|
+
;; (at your option) any later version.
|
14
|
+
|
15
|
+
;; GNU Emacs is distributed in the hope that it will be useful,
|
16
|
+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
+
;; GNU General Public License for more details.
|
19
|
+
|
20
|
+
;; You should have received a copy of the GNU General Public License
|
21
|
+
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
22
|
+
|
23
|
+
;;; Commentary:
|
24
|
+
|
25
|
+
;; This library implements specific helpers function, needed by the Ruby
|
26
|
+
;; Gem Neruda, which offers an easy way to publish a static website
|
27
|
+
;; using Org files as sources.
|
28
|
+
|
29
|
+
;;; Code:
|
30
|
+
|
31
|
+
(require 'org)
|
32
|
+
(require 'ox-html)
|
33
|
+
|
34
|
+
;;; Function Declarations
|
35
|
+
|
36
|
+
(defvar neruda/version ""
|
37
|
+
"Version of the current neruda installation")
|
38
|
+
|
39
|
+
(defvar neruda/current-work-dir nil
|
40
|
+
"Location of the current neruda website base directory.")
|
41
|
+
|
42
|
+
(defvar neruda/org-temp-dir nil
|
43
|
+
"Location of the local Org temporary directory (where to place
|
44
|
+
org timestamps and id locations).")
|
45
|
+
|
46
|
+
(defun neruda/org-html-format-spec (upstream info)
|
47
|
+
"Return format specification for preamble and postamble.
|
48
|
+
INFO is a plist used as a communication channel."
|
49
|
+
(let ((output (funcall upstream info)))
|
50
|
+
(push `(?A . ,(format "<span class=\"author\">%s</span>"
|
51
|
+
(org-export-data (plist-get info :author) info)))
|
52
|
+
output)
|
53
|
+
(push `(?k . ,(org-export-data (plist-get info :keywords) info)) output)
|
54
|
+
(push `(?K . ,(format "<ul class=\"keywords-list\">%s</ul>"
|
55
|
+
(mapconcat
|
56
|
+
(lambda (k) (format "<li class=\"keyword\">%s</li>" k))
|
57
|
+
(split-string (or (plist-get info :keywords) "") ",+ *")
|
58
|
+
"\n")))
|
59
|
+
output)
|
60
|
+
(push `(?l . ,(org-export-data (plist-get info :language) info)) output)
|
61
|
+
(push `(?n . ,(format "Neruda %s" neruda/version)) output)
|
62
|
+
(push `(?N . ,(format "<a href=\"https://git.umaneti.net/neruda/about/\">Neruda</a> %s" neruda/version)) output)
|
63
|
+
(push `(?x . ,(org-export-data (plist-get info :description) info)) output)
|
64
|
+
(push `(?X . ,(format "<p>%s</p>"
|
65
|
+
(org-export-data (plist-get info :description) info)))
|
66
|
+
output)))
|
67
|
+
|
68
|
+
(defun neruda/org-i18n-export (link description format)
|
69
|
+
"Export a i18n link"
|
70
|
+
(let* ((splitted-link (split-string link "|"))
|
71
|
+
(path (car splitted-link))
|
72
|
+
(desc (or description path))
|
73
|
+
(lang (cadr splitted-link)))
|
74
|
+
(pcase format
|
75
|
+
(`html (if lang
|
76
|
+
(format "<a href=\"%s\" hreflang=\"%s\">%s</a>"
|
77
|
+
path lang desc)
|
78
|
+
(format "<a href=\"%s\">%s</a>" path desc)))
|
79
|
+
(`latex (format "\\href{%s}{%s}" path desc))
|
80
|
+
(`ascii (format "%s (%s)" desc path))
|
81
|
+
(_ path))))
|
82
|
+
|
83
|
+
(defun neruda/org-i18n-follow (link)
|
84
|
+
"Visit a i18n link"
|
85
|
+
(browse-url (car (split-string link "|"))))
|
86
|
+
|
87
|
+
(org-link-set-parameters "i18n"
|
88
|
+
:export #'neruda/org-i18n-export
|
89
|
+
:follow #'neruda/org-i18n-follow)
|
90
|
+
|
91
|
+
|
92
|
+
;;; Set configuration options
|
93
|
+
|
94
|
+
(setq neruda/org-temp-dir (expand-file-name "tmp" neruda/current-work-dir)
|
95
|
+
org-publish-timestamp-directory (expand-file-name "timestamps/" neruda/org-temp-dir)
|
96
|
+
org-id-locations-file (expand-file-name "id-locations.el" neruda/org-temp-dir)
|
97
|
+
make-backup-files nil
|
98
|
+
enable-local-variables :all
|
99
|
+
org-confirm-babel-evaluate nil
|
100
|
+
org-export-with-broken-links t
|
101
|
+
org-html-doctype "html5"
|
102
|
+
org-html-html5-fancy t
|
103
|
+
org-html-htmlize-output-type 'css
|
104
|
+
org-html-text-markup-alist '((bold . "<strong>%s</strong>")
|
105
|
+
(code . "<code>%s</code>")
|
106
|
+
(italic . "<em>%s</em>")
|
107
|
+
(strike-through . "<del>%s</del>")
|
108
|
+
(underline . "<span class=\"underline\">%s</span>")
|
109
|
+
(verbatim . "<code>%s</code>")))
|
110
|
+
(advice-add 'org-html-format-spec :around #'neruda/org-html-format-spec)
|
111
|
+
|
112
|
+
(provide 'ox-neruda)
|
113
|
+
|
114
|
+
;;; ox-neruda.el ends here
|
data/lib/neruda/emacs.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'neruda/config'
|
4
|
+
|
5
|
+
module Neruda
|
6
|
+
# Wraps Gnu/Emacs calls
|
7
|
+
class Emacs
|
8
|
+
def initialize(file_path: nil, verbose: false)
|
9
|
+
@file = file_path
|
10
|
+
@verbose = verbose
|
11
|
+
end
|
12
|
+
|
13
|
+
def publish
|
14
|
+
if @file.nil?
|
15
|
+
emacs_args = ['--eval \'(org-publish "website")\'']
|
16
|
+
else
|
17
|
+
emacs_args = ['-f org-publish-current-file']
|
18
|
+
end
|
19
|
+
call_emacs emacs_args
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def emacs_command(arguments = [])
|
25
|
+
default_emacs = Neruda::Config.settings['emacs']
|
26
|
+
emacs_cmd = [default_emacs || 'emacs -Q --batch -nw']
|
27
|
+
emacs_cmd << '--eval \'(setq enable-dir-local-variables nil)\''
|
28
|
+
emacs_cmd << '--eval \'(setq inhibit-message t)\'' unless @verbose
|
29
|
+
emacs_cmd << '-l ./org-config.el'
|
30
|
+
emacs_cmd << "--eval '(find-file \"#{@file}\")'" unless @file.nil?
|
31
|
+
emacs_cmd.concat(arguments)
|
32
|
+
emacs_cmd.join(' ')
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_emacs(arguments = [])
|
36
|
+
command = emacs_command arguments
|
37
|
+
if @verbose
|
38
|
+
warn command
|
39
|
+
return system(command, exception: true)
|
40
|
+
end
|
41
|
+
system command, out: '/dev/null', err: '/dev/null', exception: true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/neruda/index.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'neruda/config'
|
6
|
+
require 'neruda/org_file'
|
7
|
+
require 'neruda/index/atom_generator'
|
8
|
+
require 'neruda/index/org_generator'
|
9
|
+
|
10
|
+
module Neruda
|
11
|
+
# Generates website indexes and atom feeds for all the org documents
|
12
|
+
# keywords.
|
13
|
+
class Index
|
14
|
+
attr_reader :date
|
15
|
+
|
16
|
+
include Neruda::IndexAtomGenerator
|
17
|
+
include Neruda::IndexOrgGenerator
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@pubdir = Neruda::Config.settings['public_folder']
|
21
|
+
@index = { 'index' => [] }
|
22
|
+
@projects = {}
|
23
|
+
@tags_names = {}
|
24
|
+
@date = DateTime.now
|
25
|
+
feed
|
26
|
+
sort!
|
27
|
+
end
|
28
|
+
|
29
|
+
def entries
|
30
|
+
@index.keys.reject { |k| k == 'index' }
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
@index['index'].empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_all(verbose: true)
|
38
|
+
@index.each_key do |k|
|
39
|
+
write_org(k)
|
40
|
+
warn "Generated index file for #{k}" if verbose
|
41
|
+
write_atom(k)
|
42
|
+
warn "Generated atom feed for #{k}" if verbose
|
43
|
+
end
|
44
|
+
write_all_blog_home(verbose)
|
45
|
+
end
|
46
|
+
|
47
|
+
def sort_by(kind)
|
48
|
+
if [:name, :weight].include?(kind)
|
49
|
+
tags_sorted = sort_tags_by_name_and_weight["by_#{kind}".to_sym]
|
50
|
+
# Reverse in order to have most important or A near next prompt
|
51
|
+
# and avoid to scroll to find the beginning of the list.
|
52
|
+
return tags_sorted.map do |k|
|
53
|
+
@tags_names[k] + " (#{@index[k].length})"
|
54
|
+
end.reverse
|
55
|
+
end
|
56
|
+
raise ArgumentError, "#{kind} not in [:name, :weight]"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def feed
|
62
|
+
Neruda::Config.sources.each do |project|
|
63
|
+
next unless project['is_blog']
|
64
|
+
if project['recursive']
|
65
|
+
file_pattern = '**/*.org'
|
66
|
+
else
|
67
|
+
file_pattern = '*.org'
|
68
|
+
end
|
69
|
+
Dir.glob(file_pattern, base: project['path']).map do |s|
|
70
|
+
org_file = File.join(project['path'], s)
|
71
|
+
add_to_indexes(
|
72
|
+
Neruda::OrgFile.new(org_file, project: project)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_to_project_index(article)
|
79
|
+
project = article.project
|
80
|
+
@projects[project['name']] ||= []
|
81
|
+
@projects[project['name']] << article
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_to_indexes(article)
|
85
|
+
@index['index'] << article
|
86
|
+
add_to_project_index article
|
87
|
+
article.keywords.each do |k|
|
88
|
+
slug = Neruda::OrgFile.slug k
|
89
|
+
@tags_names[slug] = k # Overwrite is permitted
|
90
|
+
@index[slug] ||= []
|
91
|
+
@index[slug] << article
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def sort!
|
96
|
+
@index.each do |k, i|
|
97
|
+
@index[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
98
|
+
end
|
99
|
+
@projects.each do |k, i|
|
100
|
+
@projects[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def sort_tags_by_name_and_weight
|
105
|
+
tags_sorted = {}
|
106
|
+
all_keys = entries
|
107
|
+
tags_sorted[:by_name] = all_keys.sort
|
108
|
+
tags_sorted[:by_weight] = all_keys.sort do |a, b|
|
109
|
+
@index[b].length <=> @index[a].length
|
110
|
+
end
|
111
|
+
tags_sorted
|
112
|
+
end
|
113
|
+
|
114
|
+
def save?
|
115
|
+
return true unless empty?
|
116
|
+
Neruda::Config.sources.each do |project|
|
117
|
+
return true if project['is_blog'] && Dir.exist?(project['path'])
|
118
|
+
end
|
119
|
+
false
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|