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.
@@ -1,17 +1,17 @@
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
- `(__ALL_PROJECTS__
13
- __THEME_CONFIG__
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 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,75 +16,78 @@ 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)
51
48
  if [:name, :weight].include?(kind)
52
- return sort_tags_by_name_and_weight["by_#{kind}".to_sym]
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 sources_list(file_list)
60
- return file_list unless file_list.nil?
61
- Dir.glob(Neruda::Config.settings['blog_pattern'],
62
- base: "src/#{@blog_path}")
63
- end
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
- file_path = "src/#{@blog_path}/#{f}"
74
- 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
+ )
75
74
  end
76
- next if exclude && file_path.match(exclude)
77
- sources << file_path
78
75
  end
79
- @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
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] = [] unless @index.has_key?(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 = @index.keys.reject { |k| k == 'index' }
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") + '</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,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
- 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
+ <<~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 = "- *[[..#{article.html_file}][#{article.title}]]*"
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