neruda 0.1.0 → 0.2.2
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 +4 -4
- data/bin/pablo +71 -33
- data/lib/neruda/config.rb +59 -16
- data/lib/neruda/config/lisp_config.rb +140 -79
- data/lib/neruda/config/org-config.el +6 -6
- data/lib/neruda/config/ox-neruda.el +28 -2
- data/lib/neruda/emacs.rb +44 -0
- data/lib/neruda/index.rb +50 -36
- data/lib/neruda/index/atom_generator.rb +16 -16
- data/lib/neruda/index/org_generator.rb +63 -41
- data/lib/neruda/org_file.rb +47 -14
- data/lib/neruda/org_file/class_methods.rb +38 -21
- data/lib/neruda/org_file/extracter.rb +12 -1
- data/lib/neruda/org_file/htmlizer.rb +6 -32
- data/lib/neruda/preview.rb +9 -7
- data/lib/neruda/templater.rb +3 -2
- data/lib/neruda/utils.rb +120 -35
- data/lib/neruda/version.rb +2 -1
- data/lib/tasks/org.rake +48 -26
- data/lib/tasks/site.rake +36 -33
- data/lib/tasks/sync.rake +6 -2
- data/lib/tasks/tags.rake +19 -0
- data/locales/en.yml +20 -1
- data/locales/fr.yml +20 -1
- metadata +57 -30
- data/themes/default/css/style.css +0 -216
- data/themes/default/fonts/Yanone_Kaffeesatz_400.woff +0 -0
- data/themes/default/fonts/Yanone_Kaffeesatz_400.woff2 +0 -0
@@ -1,17 +1,17 @@
|
|
1
|
-
;; Add
|
1
|
+
;; Add org-mode to load path
|
2
2
|
(add-to-list 'load-path (expand-file-name "org-__ORG_VER__/lisp" "__WORK_DIR__"))
|
3
|
-
;; Load
|
3
|
+
;; Load last version of htmlize.el
|
4
4
|
(load-file (expand-file-name "htmlize.el" "__WORK_DIR__"))
|
5
5
|
|
6
6
|
;; Current project options
|
7
|
-
(setq neruda/
|
7
|
+
(setq neruda/version "__VERSION__"
|
8
|
+
neruda/current-work-dir "__WORK_DIR__"
|
8
9
|
user-mail-address "__AUTHOR_EMAIL__"
|
9
10
|
user-full-name "__AUTHOR_NAME__"
|
10
11
|
org-html-metadata-timestamp-format "__LONG_DATE_FMT__"
|
11
12
|
org-publish-project-alist
|
12
|
-
`(
|
13
|
-
|
14
|
-
("website" :components (__ALL_PROJECTS_NAMES__ "theme"))))
|
13
|
+
`(__ALL_PROJECTS____THEME_CONFIG__
|
14
|
+
("website" :components (__ALL_PROJECTS_NAMES__))))
|
15
15
|
|
16
16
|
;; Load neruda lib
|
17
17
|
(load-file (expand-file-name "ox-neruda.el" "__NERUDA_DIR__"))
|
@@ -33,13 +33,38 @@
|
|
33
33
|
|
34
34
|
;;; Function Declarations
|
35
35
|
|
36
|
+
(defvar neruda/version ""
|
37
|
+
"Version of the current neruda installation")
|
38
|
+
|
36
39
|
(defvar neruda/current-work-dir nil
|
37
40
|
"Location of the current neruda website base directory.")
|
38
41
|
|
39
42
|
(defvar neruda/org-temp-dir nil
|
40
|
-
"Location of the local
|
43
|
+
"Location of the local Org temporary directory (where to place
|
41
44
|
org timestamps and id locations).")
|
42
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
|
+
|
43
68
|
(defun neruda/org-i18n-export (link description format)
|
44
69
|
"Export a i18n link"
|
45
70
|
(let* ((splitted-link (split-string link "|"))
|
@@ -72,6 +97,7 @@ org timestamps and id locations).")
|
|
72
97
|
make-backup-files nil
|
73
98
|
enable-local-variables :all
|
74
99
|
org-confirm-babel-evaluate nil
|
100
|
+
org-export-with-broken-links t
|
75
101
|
org-html-doctype "html5"
|
76
102
|
org-html-html5-fancy t
|
77
103
|
org-html-htmlize-output-type 'css
|
@@ -81,7 +107,7 @@ org timestamps and id locations).")
|
|
81
107
|
(strike-through . "<del>%s</del>")
|
82
108
|
(underline . "<span class=\"underline\">%s</span>")
|
83
109
|
(verbatim . "<code>%s</code>")))
|
84
|
-
|
110
|
+
(advice-add 'org-html-format-spec :around #'neruda/org-html-format-spec)
|
85
111
|
|
86
112
|
(provide 'ox-neruda)
|
87
113
|
|
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
CHANGED
@@ -16,75 +16,78 @@ module Neruda
|
|
16
16
|
include Neruda::IndexAtomGenerator
|
17
17
|
include Neruda::IndexOrgGenerator
|
18
18
|
|
19
|
-
def initialize
|
20
|
-
@blog_path = Neruda::Config.settings['blog_path']
|
19
|
+
def initialize
|
21
20
|
@pubdir = Neruda::Config.settings['public_folder']
|
22
21
|
@index = { 'index' => [] }
|
22
|
+
@projects = {}
|
23
23
|
@tags_names = {}
|
24
24
|
@date = DateTime.now
|
25
|
-
|
26
|
-
|
27
|
-
else
|
28
|
-
@sources = sources_list(file_list)
|
29
|
-
filter_and_prefix_sources!
|
30
|
-
@sources.each { |f| add_to_indexes(Neruda::OrgFile.new(f)) }
|
31
|
-
sort!
|
32
|
-
end
|
25
|
+
feed
|
26
|
+
sort!
|
33
27
|
end
|
34
28
|
|
35
29
|
def entries
|
36
|
-
@index.keys
|
30
|
+
@index.keys.reject { |k| k == 'index' }
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty?
|
34
|
+
@index['index'].empty?
|
37
35
|
end
|
38
36
|
|
39
|
-
def write_all(verbose
|
40
|
-
@index.
|
37
|
+
def write_all(verbose: true)
|
38
|
+
@index.each_key do |k|
|
41
39
|
write_org(k)
|
42
40
|
warn "Generated index file for #{k}" if verbose
|
43
41
|
write_atom(k)
|
44
42
|
warn "Generated atom feed for #{k}" if verbose
|
45
43
|
end
|
46
|
-
|
47
|
-
warn 'Generated all tags index' if verbose
|
44
|
+
write_all_blog_home(verbose)
|
48
45
|
end
|
49
46
|
|
50
47
|
def sort_by(kind)
|
51
48
|
if [:name, :weight].include?(kind)
|
52
|
-
|
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
|
53
55
|
end
|
54
56
|
raise ArgumentError, "#{kind} not in [:name, :weight]"
|
55
57
|
end
|
56
58
|
|
57
59
|
private
|
58
60
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def filter_and_prefix_sources!
|
66
|
-
exclude = Neruda::Config.settings['exclude_pattern']
|
67
|
-
sources = []
|
68
|
-
@sources.each do |f|
|
69
|
-
next if f == 'index.org'
|
70
|
-
if File.exist?(f)
|
71
|
-
file_path = f
|
61
|
+
def feed
|
62
|
+
Neruda::Config.sources.each do |project|
|
63
|
+
next unless project['is_blog']
|
64
|
+
if project['recursive']
|
65
|
+
file_pattern = '**/*.org'
|
72
66
|
else
|
73
|
-
|
74
|
-
|
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
|
+
)
|
75
74
|
end
|
76
|
-
next if exclude && file_path.match(exclude)
|
77
|
-
sources << file_path
|
78
75
|
end
|
79
|
-
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_to_project_index(article)
|
79
|
+
project = article.project
|
80
|
+
@projects[project['name']] ||= []
|
81
|
+
@projects[project['name']] << article
|
80
82
|
end
|
81
83
|
|
82
84
|
def add_to_indexes(article)
|
83
85
|
@index['index'] << article
|
86
|
+
add_to_project_index article
|
84
87
|
article.keywords.each do |k|
|
85
88
|
slug = Neruda::OrgFile.slug k
|
86
89
|
@tags_names[slug] = k # Overwrite is permitted
|
87
|
-
@index[slug]
|
90
|
+
@index[slug] ||= []
|
88
91
|
@index[slug] << article
|
89
92
|
end
|
90
93
|
end
|
@@ -93,16 +96,27 @@ module Neruda
|
|
93
96
|
@index.each do |k, i|
|
94
97
|
@index[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
95
98
|
end
|
99
|
+
@projects.each do |k, i|
|
100
|
+
@projects[k] = i.sort { |a, b| b.timekey <=> a.timekey }
|
101
|
+
end
|
96
102
|
end
|
97
103
|
|
98
104
|
def sort_tags_by_name_and_weight
|
99
105
|
tags_sorted = {}
|
100
|
-
all_keys =
|
106
|
+
all_keys = entries
|
101
107
|
tags_sorted[:by_name] = all_keys.sort
|
102
108
|
tags_sorted[:by_weight] = all_keys.sort do |a, b|
|
103
109
|
@index[b].length <=> @index[a].length
|
104
110
|
end
|
105
111
|
tags_sorted
|
106
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
|
107
121
|
end
|
108
122
|
end
|
@@ -11,11 +11,11 @@ module Neruda
|
|
11
11
|
@index[index_name][0...10].each do |article|
|
12
12
|
content << atom_entry(article)
|
13
13
|
end
|
14
|
-
content.join("\n")
|
14
|
+
format '%<content>s</feed>', content: content.join("\n")
|
15
15
|
end
|
16
16
|
|
17
17
|
def write_atom(index_name)
|
18
|
-
return
|
18
|
+
return unless save?
|
19
19
|
slug = Neruda::OrgFile.slug index_name
|
20
20
|
FileUtils.mkdir_p "#{@pubdir}/feeds"
|
21
21
|
atomdest = "#{@pubdir}/feeds/#{slug}.xml"
|
@@ -31,17 +31,17 @@ module Neruda
|
|
31
31
|
def atom_header(title)
|
32
32
|
domain = Neruda::Config.settings['domain']
|
33
33
|
upddate = @date.rfc3339
|
34
|
-
slug = Neruda::OrgFile.slug(title)
|
35
|
-
tagurl = "#{domain}/tags/#{slug}.html"
|
36
34
|
if title == 'index'
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
slug = 'index'
|
36
|
+
tagurl = domain
|
37
|
+
title = Neruda::Config.settings['title'] || \
|
38
|
+
R18n.t.neruda.index.all_tags
|
39
|
+
else
|
40
|
+
slug = Neruda::OrgFile.slug(title)
|
41
|
+
tagurl = "#{domain}/tags/#{slug}.html"
|
42
42
|
title = @tags_names[title]
|
43
43
|
end
|
44
|
-
|
44
|
+
title = CGI.escapeHTML(title)
|
45
45
|
<<~ENDATOM
|
46
46
|
<?xml version="1.0" encoding="utf-8"?>
|
47
47
|
<feed xmlns="http://www.w3.org/2005/Atom"
|
@@ -49,13 +49,13 @@ module Neruda
|
|
49
49
|
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
|
50
50
|
xml:lang="#{Neruda::Config.settings['lang']}">
|
51
51
|
|
52
|
-
<title>#{
|
52
|
+
<title>#{title}</title>
|
53
53
|
<link href="#{domain}/feeds/#{slug}.xml" rel="self" type="application/atom+xml"/>
|
54
|
-
<link href="#{tagurl}" rel="alternate" type="text/html" title="#{
|
54
|
+
<link href="#{tagurl}" rel="alternate" type="text/html" title="#{title}"/>
|
55
55
|
<updated>#{upddate}</updated>
|
56
56
|
<author><name>#{Neruda::Config.settings['author'] || ''}</name></author>
|
57
57
|
<id>urn:md5:#{Digest::MD5.hexdigest(domain)}</id>
|
58
|
-
<generator uri="https://
|
58
|
+
<generator uri="https://git.umaneti.net/neruda/about/">Neruda</generator>
|
59
59
|
ENDATOM
|
60
60
|
end
|
61
61
|
|
@@ -69,12 +69,12 @@ module Neruda
|
|
69
69
|
"<dc:subject>#{CGI.escapeHTML(k)}</dc:subject>"
|
70
70
|
end.join
|
71
71
|
keywords += "\n " if keywords != ''
|
72
|
-
|
72
|
+
title = CGI.escapeHTML(article.title)
|
73
73
|
<<~ENDENTRY
|
74
74
|
<entry>
|
75
|
-
<title>#{
|
75
|
+
<title>#{title}</title>
|
76
76
|
<link href="#{article.url}" rel="alternate" type="text/html"
|
77
|
-
title="#{
|
77
|
+
title="#{title}"/>
|
78
78
|
<id>urn:md5:#{Digest::MD5.hexdigest(article.timekey)}</id>
|
79
79
|
<published>#{article.datestring(:rfc3339)}</published>
|
80
80
|
<author><name>#{CGI.escapeHTML(article.author)}</name></author>
|
@@ -4,73 +4,95 @@ module Neruda
|
|
4
4
|
# Embeds methods responsible for generating an org file for a given
|
5
5
|
# index.
|
6
6
|
module IndexOrgGenerator
|
7
|
-
def to_org(index_name = 'index')
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
if year != last_year
|
13
|
-
content << ''
|
14
|
-
content << org_title(year)
|
15
|
-
last_year = year
|
16
|
-
end
|
17
|
-
content << org_entry(article)
|
18
|
-
end
|
19
|
-
content.join("\n")
|
7
|
+
def to_org(index_name = 'index', is_project: false)
|
8
|
+
return project_home_page(index_name) if is_project
|
9
|
+
return all_tags_index if index_name == 'index'
|
10
|
+
[org_header(index_name),
|
11
|
+
org_articles(@index[index_name])].join("\n")
|
20
12
|
end
|
21
13
|
alias_method :to_s, :to_org
|
22
14
|
|
23
15
|
def write_org(index_name)
|
24
|
-
return
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
return unless save?
|
17
|
+
slug = Neruda::OrgFile.slug index_name
|
18
|
+
FileUtils.mkdir 'tags' unless Dir.exist? 'tags'
|
19
|
+
content = to_org index_name
|
20
|
+
orgdest = "tags/#{slug}.org"
|
21
|
+
IO.write(orgdest, content)
|
28
22
|
end
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
content = [org_header(
|
24
|
+
private
|
25
|
+
|
26
|
+
def project_home_page(project_name)
|
27
|
+
content = [org_header(project_name, is_tag: false)]
|
28
|
+
if @projects[project_name]&.any?
|
29
|
+
content += org_articles(@projects[project_name])
|
30
|
+
end
|
31
|
+
content.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_all_blog_home(verbose)
|
35
|
+
Neruda::Config.sources.each do |project|
|
36
|
+
next unless project['is_blog']
|
37
|
+
next unless Dir.exist?(project['path'])
|
38
|
+
warn "Generated blog home for #{project['name']}" if verbose
|
39
|
+
orgdest = format('%<root>s/index.org', root: project['path'])
|
40
|
+
IO.write(orgdest, to_org(project['name'], is_project: true))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_tags_index
|
45
|
+
content = [
|
46
|
+
org_header(R18n.t.neruda.index.all_tags, is_tag: false)
|
47
|
+
]
|
34
48
|
sort_tags_by_name_and_weight.each do |t, tags|
|
35
49
|
content << ''
|
36
50
|
content << org_title(R18n.t.neruda.index.send(t), 'index-tags')
|
51
|
+
next if tags.empty?
|
37
52
|
tags.each do |k|
|
38
|
-
|
39
|
-
link = "[[#{domain}/tags/#{k}.html][#{title}]]"
|
40
|
-
content << "- #{link} (#{@index[k].length})"
|
53
|
+
content << "- #{tag_published_url(k)} (#{@index[k].length})"
|
41
54
|
end
|
42
55
|
end
|
43
|
-
|
44
|
-
src = 'src/tags/index.org'
|
45
|
-
IO.write(src, content.join("\n"))
|
56
|
+
content.join("\n")
|
46
57
|
end
|
47
58
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
src[1] = @blog_path if slug == 'index'
|
54
|
-
src.join('/')
|
59
|
+
def tag_published_url(tag_name)
|
60
|
+
domain = Neruda::Config.settings['domain']
|
61
|
+
title = @tags_names[tag_name]
|
62
|
+
tag_link = "#{domain}/tags/#{tag_name}.html"
|
63
|
+
"[[#{tag_link}][#{title}]]"
|
55
64
|
end
|
56
65
|
|
57
|
-
def org_header(title = nil)
|
58
|
-
if
|
59
|
-
title = Neruda::Config.settings['title']
|
60
|
-
elsif @tags_names.has_key?(title)
|
66
|
+
def org_header(title = nil, is_tag: true)
|
67
|
+
if is_tag
|
61
68
|
title = @tags_names[title]
|
69
|
+
elsif title.nil? || title == 'index'
|
70
|
+
title = Neruda::Config.settings['title']
|
62
71
|
end
|
63
|
-
<<~HEADER
|
72
|
+
<<~HEADER.strip
|
64
73
|
#+title: #{title}
|
65
74
|
#+author: #{Neruda::Config.settings['author']}
|
66
75
|
#+language: #{Neruda::Config.settings['lang']}
|
67
76
|
HEADER
|
68
77
|
end
|
69
78
|
|
79
|
+
def org_articles(articles_list)
|
80
|
+
last_year = nil
|
81
|
+
articles_list.map do |article|
|
82
|
+
year_title = ''
|
83
|
+
year = article.timekey.slice(0, 4)
|
84
|
+
if year != last_year
|
85
|
+
year_title = format("\n%<title>s\n", title: org_title(year))
|
86
|
+
last_year = year
|
87
|
+
end
|
88
|
+
year_title + org_entry(article)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
70
92
|
def org_entry(article)
|
71
|
-
line = "- *[[
|
93
|
+
line = "- *[[#{article.url}][#{article.title}]]*"
|
72
94
|
if article.date
|
73
|
-
art_date = article.datestring(:full, false)
|
95
|
+
art_date = article.datestring(:full, year: false)
|
74
96
|
published = R18n.t.neruda.index.published_on art_date
|
75
97
|
line += " / #{published}"
|
76
98
|
end
|