neruda 0.1.3 → 0.2.0

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