neruda 0.1.3 → 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.
@@ -1,17 +1,18 @@
1
- ;; Add custom org mode to load path
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 modern version of htmlize.el
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/current-work-dir "__WORK_DIR__"
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
  `(__ALL_PROJECTS__
13
14
  __THEME_CONFIG__
14
- ("website" :components (__ALL_PROJECTS_NAMES__ "theme"))))
15
+ ("website" :components (__ALL_PROJECTS_NAMES__))))
15
16
 
16
17
  ;; Load neruda lib
17
18
  (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 org-mode temporary directory (where to place
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
 
@@ -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
@@ -16,35 +16,32 @@ module Neruda
16
16
  include Neruda::IndexAtomGenerator
17
17
  include Neruda::IndexOrgGenerator
18
18
 
19
- def initialize(file_list = nil)
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
- if @blog_path.nil?
26
- @sources = []
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 = true)
40
- @index.keys.each do |k|
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
- write_org_lists
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)
@@ -61,35 +58,36 @@ module Neruda
61
58
 
62
59
  private
63
60
 
64
- def sources_list(file_list)
65
- return file_list unless file_list.nil?
66
- Dir.glob(Neruda::Config.settings['blog_pattern'],
67
- base: "src/#{@blog_path}")
68
- end
69
-
70
- def filter_and_prefix_sources!
71
- exclude = Neruda::Config.settings['exclude_pattern']
72
- sources = []
73
- @sources.each do |f|
74
- next if f == 'index.org'
75
- if File.exist?(f)
76
- 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'
77
66
  else
78
- file_path = "src/#{@blog_path}/#{f}"
79
- next unless File.exist?(file_path)
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
+ )
80
74
  end
81
- next if exclude && file_path.match(exclude)
82
- sources << file_path
83
75
  end
84
- @sources = sources
76
+ end
77
+
78
+ def add_to_project_index(article)
79
+ project = article.project
80
+ @projects[project['name']] ||= []
81
+ @projects[project['name']] << article
85
82
  end
86
83
 
87
84
  def add_to_indexes(article)
88
85
  @index['index'] << article
86
+ add_to_project_index article
89
87
  article.keywords.each do |k|
90
88
  slug = Neruda::OrgFile.slug k
91
89
  @tags_names[slug] = k # Overwrite is permitted
92
- @index[slug] = [] unless @index.has_key?(slug)
90
+ @index[slug] ||= []
93
91
  @index[slug] << article
94
92
  end
95
93
  end
@@ -98,16 +96,27 @@ module Neruda
98
96
  @index.each do |k, i|
99
97
  @index[k] = i.sort { |a, b| b.timekey <=> a.timekey }
100
98
  end
99
+ @projects.each do |k, i|
100
+ @projects[k] = i.sort { |a, b| b.timekey <=> a.timekey }
101
+ end
101
102
  end
102
103
 
103
104
  def sort_tags_by_name_and_weight
104
105
  tags_sorted = {}
105
- all_keys = @index.keys.reject { |k| k == 'index' }
106
+ all_keys = entries
106
107
  tags_sorted[:by_name] = all_keys.sort
107
108
  tags_sorted[:by_weight] = all_keys.sort do |a, b|
108
109
  @index[b].length <=> @index[a].length
109
110
  end
110
111
  tags_sorted
111
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
112
121
  end
113
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") + '</feed>'
14
+ format '%<content>s</feed>', content: content.join("\n")
15
15
  end
16
16
 
17
17
  def write_atom(index_name)
18
- return 0 if @blog_path.nil?
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
- if Neruda::Config.settings['title']
38
- title = Neruda::Config.settings['title']
39
- end
40
- tagurl = "#{domain}/#{@blog_path}"
41
- elsif @tags_names.has_key?(title)
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
- title_esc = CGI.escapeHTML(title)
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>#{title_esc}</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="#{title_esc}"/>
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://fossil.deparis.io/neruda">Neruda</generator>
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
- title_esc = CGI.escapeHTML(article.title)
72
+ title = CGI.escapeHTML(article.title)
73
73
  <<~ENDENTRY
74
74
  <entry>
75
- <title>#{title_esc}</title>
75
+ <title>#{title}</title>
76
76
  <link href="#{article.url}" rel="alternate" type="text/html"
77
- title="#{title_esc}"/>
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,96 @@ 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
- content = [org_header(index_name).strip]
9
- last_year = nil
10
- @index[index_name].each do |article|
11
- year = article.timekey.slice(0, 4)
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 0 if @blog_path.nil?
25
- FileUtils.mkdir_p 'src/tags' unless index_name == 'index'
26
- src = index_source_path(index_name)
27
- IO.write(src, to_org(index_name))
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
- def write_org_lists
31
- return 0 if @blog_path.nil?
32
- domain = Neruda::Config.settings['domain']
33
- content = [org_header(R18n.t.neruda.index.all_tags)]
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
- title = @tags_names[k] || k
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
- FileUtils.mkdir_p 'src/tags'
44
- src = 'src/tags/index.org'
45
- IO.write(src, content.join("\n"))
56
+ content.join("\n")
46
57
  end
47
58
 
48
- private
49
-
50
- def index_source_path(index_name)
51
- slug = Neruda::OrgFile.slug index_name
52
- src = ['src', 'tags', "#{slug}.org"]
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 title.nil? || title == 'index'
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
+ head = <<~HEADER
64
73
  #+title: #{title}
65
74
  #+author: #{Neruda::Config.settings['author']}
66
75
  #+language: #{Neruda::Config.settings['lang']}
67
76
  HEADER
77
+ head.strip
78
+ end
79
+
80
+ def org_articles(articles_list)
81
+ last_year = nil
82
+ articles_list.map do |article|
83
+ year_title = ''
84
+ year = article.timekey.slice(0, 4)
85
+ if year != last_year
86
+ year_title = format("\n%<title>s\n", title: org_title(year))
87
+ last_year = year
88
+ end
89
+ year_title + org_entry(article)
90
+ end
68
91
  end
69
92
 
70
93
  def org_entry(article)
71
- line = "- *[[..#{article.html_file}][#{article.title}]]*"
94
+ line = "- *[[#{article.url}][#{article.title}]]*"
72
95
  if article.date
73
- art_date = article.datestring(:full, false)
96
+ art_date = article.datestring(:full, year: false)
74
97
  published = R18n.t.neruda.index.published_on art_date
75
98
  line += " / #{published}"
76
99
  end