neruda 0.2.4 → 0.2.6

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,260 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'open-uri'
5
- require 'neruda/version'
6
-
7
- module Neruda
8
- # This module contains utilitary methods to ease ~org-config.el~
9
- # file generation
10
- module LispConfig
11
- # Fetch and return the last published version of Org.
12
- #
13
- # @return [String] the new x.x.x version string of Org
14
- def org_last_version
15
- return @org_version if @org_version
16
- if File.exist?('tmp/__last_org_version__')
17
- @org_version = IO.read('tmp/__last_org_version__')
18
- return @org_version
19
- end
20
- versions = JSON.parse(
21
- URI('https://updates.orgmode.org/data/releases').open.read
22
- ).sort { |a, b| b['date'] <=> a['date'] }
23
- @org_version = versions.first['version']
24
- FileUtils.mkdir_p 'tmp'
25
- IO.write('tmp/__last_org_version__', @org_version)
26
- @org_version
27
- end
28
-
29
- # Generate emacs lisp configuration file for Org and write it.
30
- #
31
- # This method saves the generated configuration in the file
32
- # ~org-config.el~ at the root of your project, overwriting it if it
33
- # existed already.
34
- #
35
- # @return [Integer] the length written (as returned by the
36
- # underlying ~IO.write~ method call)
37
- def write_org_lisp_config(with_tags: false)
38
- projects = org_generate_projects(with_tags: with_tags)
39
- workdir = Dir.pwd
40
- content = IO.read(File.expand_path('./org-config.el', __dir__))
41
- .gsub('__VERSION__', Neruda::VERSION)
42
- .gsub('__WORK_DIR__', workdir)
43
- .gsub('__NERUDA_DIR__', __dir__)
44
- .gsub('__ORG_VER__', org_last_version)
45
- .gsub('__ALL_PROJECTS__', all_projects(projects))
46
- .gsub('__THEME_CONFIG__', org_default_theme_config)
47
- .gsub('__ALL_PROJECTS_NAMES__', project_names(projects))
48
- .gsub('__LONG_DATE_FMT__', r18n_full_datetime_format)
49
- .gsub('__AUTHOR_EMAIL__', settings['author_email'] || '')
50
- .gsub('__AUTHOR_NAME__', settings['author'])
51
- IO.write("#{workdir}/org-config.el", content)
52
- end
53
-
54
- # Generate emacs directory variables file.
55
- #
56
- # This method generate the file ~.dir-locals.el~, which is
57
- # responsible to load neruda Org settings when visiting an Org file
58
- # of this neruda instance.
59
- #
60
- # @return [Integer] the length written (as returned by the
61
- # underlying ~IO.write~ method call)
62
- def write_dir_locals
63
- workdir = Dir.pwd
64
- IO.write(
65
- "#{workdir}/.dir-locals.el",
66
- "((org-mode . ((eval . (load-file \"#{workdir}/org-config.el\")))))"
67
- )
68
- end
69
-
70
- private
71
-
72
- def r18n_full_datetime_format
73
- locale = R18n.get.locale
74
- date_fmt = R18n.t.neruda.index.full_date_format(
75
- date: locale.full_format
76
- )
77
- date_fmt = locale.year_format.sub('_', date_fmt)
78
- time_fmt = locale.time_format.delete('_').strip
79
- R18n.t.neruda.index.full_date_with_time_format(
80
- date: date_fmt, time: time_fmt
81
- )
82
- end
83
-
84
- def ruby_to_lisp_boolean(value)
85
- return 't' if value == true
86
- 'nil'
87
- end
88
-
89
- def project_names(projects)
90
- names = projects.keys.map do |p|
91
- ["\"#{p}\"", "\"#{p}-assets\""]
92
- end.flatten
93
- unless settings['theme'] == 'default'
94
- names << "\"theme-#{settings['theme']}\""
95
- end
96
- sources.each do |s|
97
- # Default theme defined in settings is already included
98
- next unless s['theme'] && s['theme'] != settings['theme']
99
- # Never include theme named 'default' as it does not rely on any
100
- # file to export.
101
- next if s['theme'] == 'default'
102
- theme = "\"theme-#{s['theme']}\""
103
- next if names.include? theme
104
- names << theme
105
- end
106
- names.join(' ')
107
- end
108
-
109
- def all_projects(projects)
110
- projects.values.join("\n").strip
111
- .gsub(/\n\s*\n/, "\n")
112
- .gsub(/\n/, "\n ")
113
- end
114
-
115
- # Return the full path to the publication path of a given project
116
- # configuration.
117
- #
118
- # @param project [Hash] a project configuration (as extracted from
119
- # the ~sources~ key)
120
- # @return [String] the full path to the target dir of this project
121
- def publication_path(project)
122
- publish_in = [Dir.pwd, settings['public_folder']]
123
- publish_in << project['target'] unless project['target'] == '.'
124
- publish_in.join('/')
125
- end
126
-
127
- def org_project(project_name, opts)
128
- publish_in = publication_path(opts)
129
- other_lines = [
130
- format(':recursive %<value>s',
131
- value: ruby_to_lisp_boolean(opts['recursive']))
132
- ]
133
- if opts['exclude']
134
- other_lines << format(':exclude "%<value>s"',
135
- value: opts['exclude'])
136
- end
137
- themeconf = org_theme_config(opts['theme'])
138
- <<~ORGPROJECT
139
- ("#{project_name}"
140
- :base-directory "#{opts['path']}"
141
- :base-extension "org"
142
- #{other_lines.join("\n ")}
143
- :publishing-directory "#{publish_in}"
144
- :publishing-function org-html-publish-to-html
145
- #{opts['org_headers']})
146
- ("#{project_name}-assets"
147
- :base-directory "#{opts['path']}"
148
- :base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|svg\\\\\\|pdf"
149
- #{other_lines[0]}
150
- :publishing-directory "#{publish_in}"
151
- :publishing-function org-publish-attachment)
152
- #{themeconf}
153
- ORGPROJECT
154
- end
155
-
156
- def org_default_postamble
157
- <<~POSTAMBLE
158
- <p><span class="author">#{R18n.t.neruda.org.postamble.written_by}</span>
159
- #{R18n.t.neruda.org.postamble.with_emacs}</p>
160
- <p class="date">#{R18n.t.neruda.org.postamble.last_modification}</p>
161
- <p class="validation">%v</p>
162
- POSTAMBLE
163
- end
164
-
165
- def org_default_html_head
166
- <<~HTMLHEAD
167
- <link rel="stylesheet" type="text/css" media="screen"
168
- href="__DOMAIN__/assets/__THEME__/css/style.css">
169
- <link rel="stylesheet" type="text/css" media="screen"
170
- href="__DOMAIN__/assets/__THEME__/css/htmlize.css">
171
- __ATOM_FEED__
172
- HTMLHEAD
173
- end
174
-
175
- def org_default_html_options(project)
176
- defaults = {
177
- 'section-numbers' => 'nil',
178
- 'with-toc' => 'nil',
179
- 'html-postamble' => org_default_postamble,
180
- 'html-head' => '__ATOM_FEED__',
181
- 'html-head-include-default-style' => 't',
182
- 'html-head-include-scripts' => 't'
183
- }
184
- curtheme = project['theme'] || settings['theme']
185
- return defaults if curtheme.nil? || curtheme == 'default'
186
- defaults['html-head'] = org_default_html_head
187
- defaults['html-head-include-default-style'] = 'nil'
188
- defaults['html-head-include-scripts'] = 'nil'
189
- defaults
190
- end
191
-
192
- def expand_vars_in_html_head(head, project)
193
- curtheme = project['theme'] || settings['theme']
194
- # Head may be frozen when coming from settings
195
- head = head.gsub('__THEME__', curtheme)
196
- .gsub('__DOMAIN__', settings['domain'])
197
- return head.gsub('__ATOM_FEED__', '') unless project['is_blog']
198
- atomfeed = <<~ATOMFEED
199
- <link rel="alternate" type="application/atom+xml" title="Atom 1.0"
200
- href="#{settings['domain']}/feeds/index.xml" />
201
- ATOMFEED
202
- head.gsub('__ATOM_FEED__', atomfeed)
203
- end
204
-
205
- def build_project_org_headers(project)
206
- orgtplopts = org_default_html_options(project).merge(
207
- settings['org-html'] || {}, project['org-html'] || {}
208
- )
209
- orgtpl = []
210
- truthy_val = ['t', 'nil', '1'].freeze
211
- orgtplopts.each do |k, v|
212
- v = expand_vars_in_html_head(v, project) if k == 'html-head'
213
- val = v.strip.gsub(/"/, '\"')
214
- if truthy_val.include? val
215
- orgtpl << ":#{k} #{val}"
216
- else
217
- orgtpl << ":#{k} \"#{val}\""
218
- end
219
- end
220
- orgtpl.join("\n ")
221
- end
222
-
223
- def org_generate_projects(with_tags: false)
224
- projects = {}
225
- projects_sources = sources
226
- if with_tags
227
- tags_conf = build_source('tags')
228
- tags_conf['recursive'] = false
229
- projects_sources << tags_conf
230
- end
231
- projects_sources.each do |opts|
232
- opts['org_headers'] = build_project_org_headers(opts)
233
- projects[opts['name']] = org_project(opts['name'], opts)
234
- end
235
- projects
236
- end
237
-
238
- def org_default_theme_config
239
- theme_config = org_theme_config(settings['theme'])
240
- return theme_config if theme_config == ''
241
- output = theme_config.split("\n").map do |line|
242
- " #{line}"
243
- end
244
- format("\n%<conf>s", conf: output.join("\n"))
245
- end
246
-
247
- def org_theme_config(theme)
248
- return '' if theme.nil? || theme == 'default'
249
- workdir = Dir.pwd
250
- <<~THEMECONFIG
251
- ("theme-#{theme}"
252
- :base-directory "#{workdir}/themes/#{theme}"
253
- :base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|js\\\\\\|css\\\\\\|otf\\\\\\|ttf\\\\\\|woff2?"
254
- :recursive t
255
- :publishing-directory "#{workdir}/#{settings['public_folder']}/assets/#{theme}"
256
- :publishing-function org-publish-attachment)
257
- THEMECONFIG
258
- end
259
- end
260
- end
@@ -1,17 +0,0 @@
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____THEME_CONFIG__
14
- ("website" :components (__ALL_PROJECTS_NAMES__))))
15
-
16
- ;; Load neruda lib
17
- (load-file (expand-file-name "ox-neruda.el" "__NERUDA_DIR__"))
@@ -1,114 +0,0 @@
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/config.rb DELETED
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'neruda/config/lisp_config'
5
-
6
- module Neruda
7
- # Wrapper for configuration
8
- #
9
- # This class is a singleton interface, which share the static website
10
- # being build settings among different steps or tasks.
11
- #
12
- # It expects the website author to holds their custom settings in a
13
- # YAML file named ~config.yml~ available at the root of their
14
- # project.
15
- #
16
- # For example, with the given config file:
17
- #
18
- # #+begin_src
19
- # ---
20
- # title: My website
21
- # author: Alice Doe
22
- # #+end_src
23
- #
24
- # Settings will be available like this:
25
- #
26
- # #+begin_src
27
- # Neruda::Config.settings['author']
28
- # => "Alice Doe"
29
- # #+end_src
30
- class Config
31
- extend Neruda::LispConfig
32
-
33
- class << self
34
- # Access the current website settings
35
- #
36
- # If the settings have not been loaded yet, this method is
37
- # responsible for calling the one, which actually loads them.
38
- #
39
- # @return [Hash] the website settings
40
- def settings
41
- return load_settings unless @config
42
- @config
43
- end
44
-
45
- # Save the settings given as a parameter to the ~config.yml~ file.
46
- #
47
- # Not only this method overwrite the old settings, but it replace
48
- # the current shared settings with the ones given in
49
- # parameter. Later call to
50
- # {file:Neruda/Config.html#settings-class_method settings}
51
- # will, obviously, use these new settings.
52
- #
53
- # @param new_config [Hash] the settings to save
54
- # @return [Hash] the new settings after save
55
- def save(new_config)
56
- # Do not save obvious default config values. We'll always try to
57
- # save author and lang as they default on system variables,
58
- # which may be different from a system to another. Thus it may
59
- # be confusing if one use neruda on two different computer and
60
- # these params always change.
61
- new_config.delete_if do |k, v|
62
- ['domain', 'public_folder', 'templates', 'theme'].include?(k) \
63
- && v == default_settings[k]
64
- end
65
- IO.write 'config.yml', new_config.to_yaml
66
- load_settings # Reload config, taking default settings into account
67
- end
68
-
69
- # Load the given settings as if they comes from the ~config.yml~ file.
70
- #
71
- # This method is handy for testing purpose. Later call to
72
- # {file:Neruda/Config.html#settings-class_method settings} will
73
- # use these new settings.
74
- #
75
- # @param config [Hash] the settings to artificially load
76
- # @return [Hash] the new settings
77
- def load_test(config)
78
- @sources = nil # Reset sources
79
- @config = default_settings.merge config
80
- end
81
-
82
- # Return the qualified projects sources list.
83
- #
84
- # @return [Array] the fully qualified projects sources list
85
- def sources
86
- return @sources if @sources
87
- default_sources = [{ 'path' => 'src', 'target' => '.' }]
88
- @sources = (settings['sources'] || default_sources).map do |s|
89
- build_source(s)
90
- end.compact
91
- end
92
-
93
- private
94
-
95
- def load_settings
96
- @sources = nil
97
- conf_file = 'config.yml'
98
- if File.exist? conf_file
99
- @config = default_settings.merge(YAML.load_file(conf_file)).freeze
100
- else
101
- @config = default_settings
102
- end
103
- end
104
-
105
- def extract_lang_from_env(default)
106
- (ENV['LANG'] || default).split('_', 2).first
107
- end
108
-
109
- def default_settings
110
- return @default_settings if @default_settings
111
- @default_settings = {
112
- 'author' => (ENV['USER'] || ''),
113
- 'domain' => '',
114
- 'lang' => extract_lang_from_env('en'),
115
- 'public_folder' => 'public_html',
116
- 'templates' => [],
117
- 'theme' => 'default'
118
- }.freeze
119
- end
120
-
121
- def build_source(seed)
122
- opts = { 'recursive' => true, 'is_blog' => false }
123
- case seed
124
- when String
125
- opts['path'] = seed
126
- when Hash
127
- opts.merge! seed
128
- end
129
- return nil unless opts.has_key?('path')
130
- opts['path'] = File.expand_path(opts['path'])
131
- opts['name'] ||= File.basename(opts['path']).sub(/^\./, '')
132
- opts['target'] ||= opts['name']
133
- opts
134
- end
135
- end
136
- end
137
- end
data/lib/neruda/emacs.rb DELETED
@@ -1,37 +0,0 @@
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
- command = emacs_command(
15
- '-l ./org-config.el', '--eval \'(org-publish "website")\''
16
- )
17
- if @verbose
18
- warn command
19
- return system(command, exception: true)
20
- end
21
- system command, out: '/dev/null', err: '/dev/null', exception: true
22
- end
23
-
24
- private
25
-
26
- def emacs_command(*arguments)
27
- default_emacs = Neruda::Config.settings['emacs']
28
- emacs_cmd = [
29
- default_emacs || 'emacs -Q --batch -nw',
30
- '--eval \'(setq enable-dir-local-variables nil)\''
31
- ]
32
- emacs_cmd << '--eval \'(setq inhibit-message t)\'' unless @verbose
33
- emacs_cmd.concat(arguments)
34
- emacs_cmd.join(' ')
35
- end
36
- end
37
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cgi'
4
- require 'neruda/config'
5
-
6
- module Neruda
7
- # Embeds Atom feeds sepecific methods
8
- module IndexAtomGenerator
9
- def to_atom(index_name = 'index')
10
- content = [atom_header(index_name)]
11
- @index[index_name][0...10].each do |article|
12
- content << atom_entry(article)
13
- end
14
- format '%<content>s</feed>', content: content.join("\n")
15
- end
16
-
17
- def write_atom(index_name)
18
- return unless save?
19
- slug = Neruda::OrgFile.slug index_name
20
- FileUtils.mkdir_p "#{@pubdir}/feeds"
21
- atomdest = "#{@pubdir}/feeds/#{slug}.xml"
22
- IO.write(atomdest, to_atom(index_name))
23
- end
24
-
25
- private
26
-
27
- # Render the Atom feed header.
28
- #
29
- # @param title [String] the title of the current atom feed
30
- # @return [String] the Atom header as a String
31
- def atom_header(title)
32
- domain = Neruda::Config.settings['domain']
33
- upddate = @date.rfc3339
34
- if title == 'index'
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
- title = @tags_names[title]
43
- end
44
- title = CGI.escapeHTML(title)
45
- <<~ENDATOM
46
- <?xml version="1.0" encoding="utf-8"?>
47
- <feed xmlns="http://www.w3.org/2005/Atom"
48
- xmlns:dc="http://purl.org/dc/elements/1.1/"
49
- xmlns:wfw="http://wellformedweb.org/CommentAPI/"
50
- xml:lang="#{Neruda::Config.settings['lang']}">
51
-
52
- <title>#{title}</title>
53
- <link href="#{domain}/feeds/#{slug}.xml" rel="self" type="application/atom+xml"/>
54
- <link href="#{tagurl}" rel="alternate" type="text/html" title="#{title}"/>
55
- <updated>#{upddate}</updated>
56
- <author><name>#{Neruda::Config.settings['author'] || ''}</name></author>
57
- <id>urn:md5:#{Digest::MD5.hexdigest(domain)}</id>
58
- <generator uri="https://git.umaneti.net/neruda/about/">Neruda</generator>
59
- ENDATOM
60
- end
61
-
62
- # Render an Atom feed entry.
63
- #
64
- # @param article [Neruda::OrgFile] the related org document for this
65
- # entry
66
- # @return [String] the Atom entry as a String
67
- def atom_entry(article)
68
- keywords = article.keywords.map do |k|
69
- "<dc:subject>#{CGI.escapeHTML(k)}</dc:subject>"
70
- end.join
71
- keywords += "\n " if keywords != ''
72
- title = CGI.escapeHTML(article.title)
73
- <<~ENDENTRY
74
- <entry>
75
- <title>#{title}</title>
76
- <link href="#{article.url}" rel="alternate" type="text/html"
77
- title="#{title}"/>
78
- <id>urn:md5:#{Digest::MD5.hexdigest(article.timekey)}</id>
79
- <published>#{article.datestring(:rfc3339)}</published>
80
- <author><name>#{CGI.escapeHTML(article.author)}</name></author>
81
- #{keywords}<content type="html">#{CGI.escapeHTML(article.excerpt)}</content>
82
- </entry>
83
- ENDENTRY
84
- end
85
- end
86
- end