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