neruda 0.0.10 → 0.1.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.
- checksums.yaml +5 -5
- data/bin/pablo +105 -246
- data/lib/neruda/config.rb +94 -0
- data/lib/neruda/config/lisp_config.rb +201 -0
- data/lib/neruda/config/org-config.el +17 -0
- data/lib/neruda/config/ox-neruda.el +88 -0
- data/lib/neruda/index.rb +108 -0
- data/lib/neruda/index/atom_generator.rb +86 -0
- data/lib/neruda/index/org_generator.rb +92 -0
- data/lib/neruda/org_file.rb +266 -0
- data/lib/neruda/org_file/class_methods.rb +55 -0
- data/lib/neruda/org_file/extracter.rb +61 -0
- data/lib/neruda/org_file/htmlizer.rb +78 -0
- data/lib/neruda/preview.rb +53 -0
- data/lib/neruda/templater.rb +111 -0
- data/lib/neruda/utils.rb +130 -0
- data/lib/neruda/version.rb +5 -0
- data/lib/tasks/org.rake +69 -0
- data/lib/tasks/site.rake +84 -0
- data/lib/tasks/sync.rake +30 -0
- data/locales/en.yml +18 -0
- data/locales/fr.yml +18 -0
- data/themes/default/css/style.css +216 -0
- data/themes/default/fonts/Yanone_Kaffeesatz_400.woff +0 -0
- data/themes/default/fonts/Yanone_Kaffeesatz_400.woff2 +0 -0
- metadata +145 -39
- data/README.md +0 -98
- data/docs/Rakefile.example +0 -4
- data/docs/config.yml.example +0 -17
- data/lib/assets/chapter.slim +0 -14
- data/lib/assets/index.slim +0 -13
- data/lib/assets/layout.slim +0 -17
- data/lib/assets/style.css +0 -199
- data/lib/neruda.rb +0 -112
- data/lib/neruda/chapter.rb +0 -26
- data/lib/neruda/url.rb +0 -14
- data/lib/tasks/book.rake +0 -60
- data/lib/tasks/capistrano/chapters.rake +0 -60
- data/lib/tasks/capistrano/sinatra.rake +0 -18
- data/lib/tasks/chapters.rake +0 -132
- data/lib/tasks/sinatra.rake +0 -36
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'neruda/config'
|
4
|
+
|
5
|
+
module Neruda
|
6
|
+
# This module holds HTML formatter methods for the {Neruda::OrgFile}
|
7
|
+
# class.
|
8
|
+
module OrgFileHtmlizer
|
9
|
+
# Publish the current file or the entire project if
|
10
|
+
# {Neruda::OrgFile#file @file} is ~nil~.
|
11
|
+
#
|
12
|
+
# @return [Boolean, nil] the underlying ~system~ method return value
|
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
|
+
# Format {Neruda::OrgFile#keywords} list in an HTML listing.
|
25
|
+
#
|
26
|
+
# @return [String] the HTML keywords list
|
27
|
+
def keywords_to_html
|
28
|
+
domain = Neruda::Config.settings['domain']
|
29
|
+
klist = @keywords.map do |k|
|
30
|
+
<<~KEYWORDLINK
|
31
|
+
<li class="keyword">
|
32
|
+
<a href="#{domain}/tags/#{Neruda::OrgFile.slug(k)}.html">#{k}</a>
|
33
|
+
</li>
|
34
|
+
KEYWORDLINK
|
35
|
+
end.join
|
36
|
+
"<ul class=\"keywords-list\">#{klist}</ul>"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Format {Neruda::OrgFile#date} as a HTML `time` tag.
|
40
|
+
#
|
41
|
+
# @return [String] the HTML `time` tag
|
42
|
+
def date_to_html(dateformat = :full)
|
43
|
+
return '' if @date.nil?
|
44
|
+
"<time datetime=\"#{@date.rfc3339}\">#{datestring(dateformat)}</time>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Format {Neruda::OrgFile#author} in a HTML `span` tag with a
|
48
|
+
# specific class.
|
49
|
+
#
|
50
|
+
# @return [String] the author HTML `span`
|
51
|
+
def author_to_html
|
52
|
+
return '' if @author == ''
|
53
|
+
"<span class=\"author\">#{@author}</span>"
|
54
|
+
end
|
55
|
+
|
56
|
+
def emacs_command(arguments = [])
|
57
|
+
default_emacs = Neruda::Config.settings['emacs']
|
58
|
+
emacs_cmd = [default_emacs || 'emacs -Q --batch -nw']
|
59
|
+
emacs_cmd << '--eval \'(setq enable-dir-local-variables nil)\''
|
60
|
+
unless @options[:verbose]
|
61
|
+
emacs_cmd << '--eval \'(setq inhibit-message t)\''
|
62
|
+
end
|
63
|
+
emacs_cmd << '-l ./org-config.el'
|
64
|
+
emacs_cmd << "--eval '(find-file \"#{@file}\")'" unless @file.nil?
|
65
|
+
emacs_cmd.concat(arguments)
|
66
|
+
emacs_cmd.join(' ')
|
67
|
+
end
|
68
|
+
|
69
|
+
def call_emacs(arguments = [])
|
70
|
+
command = emacs_command arguments
|
71
|
+
if @options[:verbose]
|
72
|
+
warn command
|
73
|
+
return system(command)
|
74
|
+
end
|
75
|
+
system command, out: '/dev/null', err: '/dev/null'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'webrick'
|
4
|
+
require 'neruda/config'
|
5
|
+
|
6
|
+
module Neruda # rubocop:disable Style/Documentation
|
7
|
+
# A tiny preview server, which main goal is to replace references to
|
8
|
+
# the target domain by localhost.
|
9
|
+
class PreviewServlet < WEBrick::HTTPServlet::AbstractServlet
|
10
|
+
include WEBrick::HTTPUtils
|
11
|
+
|
12
|
+
def do_GET(request, response) # rubocop:disable Naming/MethodName
|
13
|
+
file = local_path(request.path)
|
14
|
+
response.body = parse_body(file, "http://#{request.host}:#{request.port}")
|
15
|
+
response.status = 200
|
16
|
+
response.content_type = mime_type(file, DefaultMimeTypes)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def local_path(requested_path)
|
22
|
+
routes = Neruda::Config.settings['routes'] || {}
|
23
|
+
return routes[requested_path] if routes.keys.include? requested_path
|
24
|
+
local_path = Neruda::Config.settings['public_folder'] + requested_path
|
25
|
+
if File.directory? local_path
|
26
|
+
local_path = local_path.delete_suffix('/') + '/index.html'
|
27
|
+
end
|
28
|
+
return local_path if File.exist? local_path
|
29
|
+
raise WEBrick::HTTPStatus::NotFound, 'Not found.'
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_body(local_path, local_host)
|
33
|
+
body = IO.read local_path
|
34
|
+
return body unless local_path.match?(/\.(?:ht|x)ml$/)
|
35
|
+
domain = Neruda::Config.settings['domain']
|
36
|
+
return body if domain == ''
|
37
|
+
body.gsub(/"file:\/\//, '"' + local_host)
|
38
|
+
.gsub(/"#{domain}/, '"' + local_host)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def start_preview
|
44
|
+
# Inspired by ruby un.rb library, which allows normally to start a
|
45
|
+
# webrick server in one line: ruby -run -e httpd public_html -p 5000
|
46
|
+
port = Neruda::Config.settings['server_port'] || 5000
|
47
|
+
s = WEBrick::HTTPServer.new(Port: port)
|
48
|
+
s.mount '/', Neruda::PreviewServlet
|
49
|
+
['TERM', 'QUIT', 'INT'].each { |sig| trap(sig, proc { s.shutdown }) }
|
50
|
+
s.start
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'neruda/org_file'
|
6
|
+
|
7
|
+
module Neruda
|
8
|
+
# Insert custom part inside generated HTML files.
|
9
|
+
class Templater
|
10
|
+
def initialize(source, dom, opts = {})
|
11
|
+
@dom = dom
|
12
|
+
@org_file = source
|
13
|
+
@position = opts['type'] || 'after'
|
14
|
+
@content = opts['content']
|
15
|
+
@element = @dom.css(opts['selector'])
|
16
|
+
digest = Digest::MD5.hexdigest(@content)
|
17
|
+
@check_line = " Neruda Template: #{digest} "
|
18
|
+
end
|
19
|
+
|
20
|
+
def apply
|
21
|
+
flag_head
|
22
|
+
content = @org_file.format(@content)
|
23
|
+
@element.each do |e|
|
24
|
+
insert_new_node_at e, content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def in_head?
|
29
|
+
@dom.xpath('//head').children.to_a.filter(&:comment?).each do |c|
|
30
|
+
return true if c.text == @check_line
|
31
|
+
end
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def customize_output(file_name, source = nil)
|
37
|
+
templates_to_apply = filter_templates(file_name)
|
38
|
+
return if templates_to_apply.empty?
|
39
|
+
if source.nil?
|
40
|
+
sourcepath = Neruda::OrgFile.source_for_target(file_name)
|
41
|
+
source = Neruda::OrgFile.new(sourcepath)
|
42
|
+
end
|
43
|
+
dom = open_dom(file_name)
|
44
|
+
templates_to_apply.each do |t|
|
45
|
+
tpl = Neruda::Templater.new(source, dom, t)
|
46
|
+
next if tpl.in_head?
|
47
|
+
tpl.apply
|
48
|
+
end
|
49
|
+
write_dom(file_name, dom)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def filter_templates(file_name)
|
55
|
+
templates = Neruda::Config.settings['templates']
|
56
|
+
return [] if templates.nil? || templates.empty?
|
57
|
+
templates.filter do |t|
|
58
|
+
if !t.has_key?('selector') || !t.has_key?('content')
|
59
|
+
false
|
60
|
+
elsif t.has_key?('path') && !check_path(file_name, t['path'])
|
61
|
+
false
|
62
|
+
else
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def open_dom(file_name)
|
69
|
+
file = File.new file_name, 'r'
|
70
|
+
dom = Nokogiri::HTML file
|
71
|
+
file.close
|
72
|
+
dom
|
73
|
+
end
|
74
|
+
|
75
|
+
def write_dom(file_name, dom)
|
76
|
+
file = File.new file_name, 'w'
|
77
|
+
dom.write_to file
|
78
|
+
file.close
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_path(file_name, pathes)
|
82
|
+
pub_folder = Neruda::Config.settings['public_folder']
|
83
|
+
if pathes.is_a?(Array)
|
84
|
+
pathes.each do |tp|
|
85
|
+
return true if File.fnmatch?("#{pub_folder}#{tp}",
|
86
|
+
file_name, File::FNM_DOTMATCH)
|
87
|
+
end
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
File.fnmatch?("#{pub_folder}#{pathes}",
|
91
|
+
file_name, File::FNM_DOTMATCH)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def flag_head
|
98
|
+
@dom.xpath('//head').first.prepend_child("<!--#{@check_line}-->\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
def insert_new_node_at(elem, content)
|
102
|
+
if @position == 'before'
|
103
|
+
elem.add_previous_sibling content
|
104
|
+
elsif @position == 'replace'
|
105
|
+
elem.replace content
|
106
|
+
else
|
107
|
+
elem.add_next_sibling content
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/neruda/utils.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rainbow'
|
4
|
+
require 'neruda/config'
|
5
|
+
|
6
|
+
module Neruda
|
7
|
+
# Embeds usefull methods, mainly used in rake tasks.
|
8
|
+
module Utils
|
9
|
+
# @return [Hash] the possible throbber themes
|
10
|
+
THROBBER_FRAMES = {
|
11
|
+
'basic' => '-\|/',
|
12
|
+
'basicdots' => '⋯⋱⋮⋰',
|
13
|
+
'moon' => '🌑🌒🌓🌔🌕🌖🌗🌘',
|
14
|
+
'clock' => '🕛🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚',
|
15
|
+
'bricks' => '⣷⣯⣟⡿⢿⣻⣽⣾',
|
16
|
+
'points' => '·⁘∷⁛∷⁘',
|
17
|
+
'quadrant2' => '▙▛▜▟',
|
18
|
+
'default' => ['⠁ ⠂ ⠄ ⡀ ⠄ ⠂ ⠁', '⠂ ⠁ ⠂ ⠄ ⡀ ⠄ ⠂', '⠄ ⠂ ⠁ ⠂ ⠄ ⡀ ⠄',
|
19
|
+
'⡀ ⠄ ⠂ ⠁ ⠂ ⠄ ⡀', '⠄ ⡀ ⠄ ⠂ ⠁ ⠂ ⠄', '⠂ ⠄ ⡀ ⠄ ⠂ ⠁ ⠂']
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# @return [Hash] the possible ~pablo~ options and their
|
23
|
+
# configuration
|
24
|
+
PABLO_OPTIONS = {
|
25
|
+
'-a' => { long: 'author' },
|
26
|
+
'-l' => { long: 'lang', keyword: 'LOCALE' },
|
27
|
+
'-t' => { long: 'title' },
|
28
|
+
'-p' => { long: 'path', desc: 'Path to the new file' },
|
29
|
+
'-d' => { long: 'directory', boolean: true,
|
30
|
+
desc: 'Wrap the new org file in a named folder' },
|
31
|
+
'-v' => { long: 'verbose', boolean: true, meth: :on_tail },
|
32
|
+
'-h' => { long: 'help', boolean: true, meth: :on_tail,
|
33
|
+
desc: 'Display help for a command and exit' },
|
34
|
+
'-V' => { long: 'version', boolean: true, meth: :on_tail,
|
35
|
+
desc: 'Display Neruda version and exit' }
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
# @return [Hash] the possible ~pablo~ subcommands and their
|
39
|
+
# configuration
|
40
|
+
PABLO_COMMANDS = {
|
41
|
+
'init' => { opts: ['-a', '-l', '-t', '-v', '-h'],
|
42
|
+
desc: 'Initialize your Neruda instance ' \
|
43
|
+
'(you just need to do it once).' },
|
44
|
+
'preview' => { opts: ['-h'],
|
45
|
+
desc: 'Start a test webserver to preview ' \
|
46
|
+
'your website on http://127.0.0.1:5000' },
|
47
|
+
'open' => { opts: ['-a', '-l', '-t', '-d', '-p', '-v', '-h'],
|
48
|
+
desc: 'Open or create an org file for edition.' },
|
49
|
+
'help' => { opts: ['-h'], desc: 'Alias for the -h switch.' },
|
50
|
+
'basic' => { opts: ['-h', '-V'], label: '<command>' }
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
class << self
|
54
|
+
# Animates strings in the user console to alert him that something
|
55
|
+
# is running in the background.
|
56
|
+
#
|
57
|
+
# The animation is chosen among a bunch of themes, with the
|
58
|
+
# configuration option ~throbber~ (retrieved via
|
59
|
+
# {Neruda::Config#settings}).
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# long_stuff = Thread.new { very_long_operation }
|
63
|
+
# Neruda::Utils.throbber(long_stuff, 'Computing hard stuff:')
|
64
|
+
#
|
65
|
+
# @param thread [Thread] the long-running operation to decorate
|
66
|
+
# @param message [String] the message to display before the throbber
|
67
|
+
# @return [void]
|
68
|
+
def throbber(thread, message)
|
69
|
+
model = Neruda::Config.settings['throbber'] || 'default'
|
70
|
+
model = 'default' unless Neruda::Utils::THROBBER_FRAMES.has_key?(model)
|
71
|
+
frames = Neruda::Utils::THROBBER_FRAMES[model]
|
72
|
+
current = 0
|
73
|
+
while thread.alive?
|
74
|
+
sleep 0.1
|
75
|
+
print "#{message} #{frames[current % frames.length]}\r"
|
76
|
+
current += 1
|
77
|
+
end
|
78
|
+
done = Rainbow('done'.ljust(frames[0].length)).green
|
79
|
+
puts "#{message} #{done}"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the short and long options specification for a given
|
83
|
+
# short option.
|
84
|
+
#
|
85
|
+
# This method use the {Neruda::Utils::PABLO_OPTIONS} Hash to
|
86
|
+
# retrieve corresponding values.
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# spec = Neruda::Utils.decorate_option('-a')
|
90
|
+
# => ['-a AUTHOR', '--author AUTHOR']
|
91
|
+
#
|
92
|
+
# @param short [String] the short option to decorate
|
93
|
+
# @return [Array] the short and long specification for an option
|
94
|
+
def decorate_option(short)
|
95
|
+
opt = Neruda::Utils::PABLO_OPTIONS[short]
|
96
|
+
long = "--#{opt[:long]}"
|
97
|
+
return [short, long] if opt[:boolean]
|
98
|
+
key = ' ' + (opt[:keyword] || opt[:long].upcase)
|
99
|
+
[short + key, long + key]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the ~pablo~ help summary for a given command.
|
103
|
+
#
|
104
|
+
# @param command [String] the command for which a summary
|
105
|
+
# should be given
|
106
|
+
# @return [String]
|
107
|
+
def summarize_command(command)
|
108
|
+
Neruda::Utils::PABLO_COMMANDS[command][:opts].map do |k|
|
109
|
+
short, long = Neruda::Utils.decorate_option(k)
|
110
|
+
opt = Neruda::Utils::PABLO_OPTIONS[k]
|
111
|
+
line = ' ' + [short, long].join(', ')
|
112
|
+
line = line.ljust(34) + " #{opt[:desc]}" if opt.has_key?(:desc)
|
113
|
+
line + "\n"
|
114
|
+
end.join
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns a formatted list of available commands for ~pablo~.
|
118
|
+
#
|
119
|
+
# @return [String]
|
120
|
+
def list_commands
|
121
|
+
lines = ''
|
122
|
+
Neruda::Utils::PABLO_COMMANDS.each do |cmd, opt|
|
123
|
+
next if cmd == 'basic'
|
124
|
+
lines += " #{cmd.ljust(10)} #{opt[:desc]}\n"
|
125
|
+
end
|
126
|
+
lines
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/tasks/org.rake
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Neruda::Config is required by Neruda::Utils
|
4
|
+
require 'neruda/utils'
|
5
|
+
|
6
|
+
namespace :org do
|
7
|
+
desc 'Download last version of org-mode'
|
8
|
+
task :download do
|
9
|
+
verbose = Rake::FileUtilsExt.verbose_flag
|
10
|
+
org_version = "org-#{Neruda::Config.org_last_version}"
|
11
|
+
next if Neruda::Config.org_last_version.nil?
|
12
|
+
next if Dir.exist?("#{org_version}/lisp")
|
13
|
+
tarball = "#{org_version}.tar.gz"
|
14
|
+
curl = ['curl', '--progress-bar', '-O',
|
15
|
+
"https://orgmode.org/#{tarball}"]
|
16
|
+
make = ['make', '-C', org_version]
|
17
|
+
unless verbose
|
18
|
+
curl[1] = '-s'
|
19
|
+
make << '-s'
|
20
|
+
make << 'EMACSQ="emacs -Q --eval \'(setq inhibit-message t)\'"'
|
21
|
+
end
|
22
|
+
build = Thread.new do
|
23
|
+
sh curl.join(' ')
|
24
|
+
sh "tar xzf #{tarball}"
|
25
|
+
File.unlink tarball
|
26
|
+
sh((make + ['compile']).join(' '))
|
27
|
+
sh((make + ['autoloads']).join(' '))
|
28
|
+
Dir.glob('org-[0-9.]*').each do |ov|
|
29
|
+
next if ov == org_version
|
30
|
+
rm_r ov
|
31
|
+
end
|
32
|
+
end
|
33
|
+
if verbose
|
34
|
+
build.join
|
35
|
+
warn "#{org_version} has been locally installed"
|
36
|
+
else
|
37
|
+
Neruda::Utils.throbber(build, 'Installing org mode:')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
file 'htmlize.el' do
|
42
|
+
verbose = Rake::FileUtilsExt.verbose_flag
|
43
|
+
curl = ['curl', '--progress-bar', '-O',
|
44
|
+
'https://raw.githubusercontent.com/hniksic/emacs-htmlize/master/htmlize.el']
|
45
|
+
curl[1] = '-s' unless verbose
|
46
|
+
build = Thread.new { sh curl.join(' ') }
|
47
|
+
if verbose
|
48
|
+
build.join
|
49
|
+
warn 'htmlize.el has been locally installed'
|
50
|
+
else
|
51
|
+
Neruda::Utils.throbber(build, 'Installing htmlize.el:')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
file 'org-config.el' => 'htmlize.el' do
|
56
|
+
next if Neruda::Config.org_last_version.nil?
|
57
|
+
Neruda::Config.write_org_lisp_config
|
58
|
+
end
|
59
|
+
|
60
|
+
file '.dir-locals.el' do
|
61
|
+
Neruda::Config.write_dir_locals
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Install org'
|
65
|
+
task install: ['org:download', 'org-config.el', '.dir-locals.el'] do
|
66
|
+
mkdir_p "#{Neruda::Config.settings['public_folder']}/assets"
|
67
|
+
mkdir_p 'src'
|
68
|
+
end
|
69
|
+
end
|