neruda 0.0.9 → 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.
- checksums.yaml +5 -5
- data/bin/pablo +135 -238
- data/lib/neruda/config.rb +137 -0
- data/lib/neruda/config/lisp_config.rb +254 -0
- data/lib/neruda/config/org-config.el +18 -0
- data/lib/neruda/config/ox-neruda.el +114 -0
- data/lib/neruda/emacs.rb +44 -0
- data/lib/neruda/index.rb +122 -0
- data/lib/neruda/index/atom_generator.rb +86 -0
- data/lib/neruda/index/org_generator.rb +115 -0
- data/lib/neruda/org_file.rb +299 -0
- data/lib/neruda/org_file/class_methods.rb +72 -0
- data/lib/neruda/org_file/extracter.rb +72 -0
- data/lib/neruda/org_file/htmlizer.rb +53 -0
- data/lib/neruda/preview.rb +55 -0
- data/lib/neruda/templater.rb +112 -0
- data/lib/neruda/utils.rb +212 -0
- data/lib/neruda/version.rb +6 -0
- data/lib/tasks/org.rake +84 -0
- data/lib/tasks/site.rake +86 -0
- data/lib/tasks/sync.rake +34 -0
- data/lib/tasks/tags.rake +19 -0
- data/locales/en.yml +37 -0
- data/locales/fr.yml +37 -0
- data/themes/default/css/htmlize.css +346 -0
- data/themes/default/css/style.css +153 -0
- data/themes/default/img/bottom.png +0 -0
- data/themes/default/img/tic.png +0 -0
- data/themes/default/img/top.png +0 -0
- metadata +153 -43
- 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 -106
- 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,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Neruda
|
4
|
+
# This module holds class methods for the {Neruda::OrgFile} class.
|
5
|
+
module OrgFileClassMethods
|
6
|
+
def source_for_target(file_name)
|
7
|
+
# file_name may be frozen...
|
8
|
+
src = file_name.sub(/\.html$/, '.org')
|
9
|
+
pubfolder = Neruda::Config.settings['public_folder']
|
10
|
+
src.sub!(/^#{pubfolder}\//, '')
|
11
|
+
# Look for match in each possible sources. The first found wins.
|
12
|
+
Neruda::Config.sources.each do |project|
|
13
|
+
if project['target'] == '.'
|
14
|
+
origin = File.join(project['path'], src)
|
15
|
+
else
|
16
|
+
origin = File.join(
|
17
|
+
project['path'], src.sub(/^#{project['target']}\//, '')
|
18
|
+
)
|
19
|
+
end
|
20
|
+
return origin if File.exist?(origin)
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def target_for_source(file_name, project, with_public_folder: true)
|
26
|
+
return nil if file_name.nil?
|
27
|
+
# file_name may be frozen...
|
28
|
+
target = file_name.sub(/\.org$/, '.html').sub(/^#{Dir.pwd}\//, '')
|
29
|
+
if project.nil?
|
30
|
+
subfolder = File.basename(File.dirname(target))
|
31
|
+
target = File.basename(target)
|
32
|
+
target = "#{subfolder}/#{target}" if subfolder != '.'
|
33
|
+
else
|
34
|
+
project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
|
35
|
+
target.sub!(/^#{project_relative_path}\//, '')
|
36
|
+
target = "#{project['target']}/#{target}" if project['target'] != '.'
|
37
|
+
end
|
38
|
+
return target unless with_public_folder
|
39
|
+
pubfolder = Neruda::Config.settings['public_folder']
|
40
|
+
"#{pubfolder}/#{target}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def project_for_source(file_name)
|
44
|
+
# Look for match in each possible sources. The first found wins.
|
45
|
+
Neruda::Config.sources.each do |project|
|
46
|
+
project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
|
47
|
+
return project if file_name =~ /^#{project_relative_path}\//
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def slug(title)
|
53
|
+
title.downcase.gsub(' ', '-')
|
54
|
+
.encode('ascii', fallback: ->(k) { translit(k) })
|
55
|
+
.gsub(/[^\w-]/, '').gsub(/-$/, '')
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def translit(char)
|
61
|
+
return 'a' if ['á', 'à', 'â', 'ä', 'ǎ', 'ã', 'å'].include?(char)
|
62
|
+
return 'e' if ['é', 'è', 'ê', 'ë', 'ě', 'ẽ'].include?(char)
|
63
|
+
return 'i' if ['í', 'ì', 'î', 'ï', 'ǐ', 'ĩ'].include?(char)
|
64
|
+
return 'o' if ['ó', 'ò', 'ô', 'ö', 'ǒ', 'õ'].include?(char)
|
65
|
+
return 'u' if ['ú', 'ù', 'û', 'ü', 'ǔ', 'ũ'].include?(char)
|
66
|
+
return 'y' if ['ý', 'ỳ', 'ŷ', 'ÿ', 'ỹ'].include?(char)
|
67
|
+
return 'c' if char == 'ç'
|
68
|
+
return 'n' if char == 'ñ'
|
69
|
+
'-'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Neruda
|
4
|
+
# This module holds extracter methods for the {Neruda::OrgFile} class.
|
5
|
+
module OrgFileExtracter
|
6
|
+
private
|
7
|
+
|
8
|
+
# Main method, which will call the other to initialize an
|
9
|
+
# {Neruda::OrgFile} instance.
|
10
|
+
def extract_data
|
11
|
+
@content = IO.read @file
|
12
|
+
@title = extract_title
|
13
|
+
@subtitle = extract_subtitle
|
14
|
+
@date = extract_date
|
15
|
+
@author = extract_author
|
16
|
+
@keywords = extract_keywords
|
17
|
+
@lang = extract_lang
|
18
|
+
@excerpt = extract_excerpt
|
19
|
+
end
|
20
|
+
|
21
|
+
def extract_date
|
22
|
+
timerx = '([0-9:]{5})(?::([0-9]{2}))?'
|
23
|
+
m = /^#\+date: *<([0-9-]{10}) [\w.]+(?: #{timerx})?> *$/i.match(@content)
|
24
|
+
return nil if m.nil?
|
25
|
+
@notime = m[2].nil?
|
26
|
+
if @notime
|
27
|
+
time = '00:00:00'
|
28
|
+
else
|
29
|
+
time = "#{m[2]}:#{m[3] || '00'}"
|
30
|
+
end
|
31
|
+
DateTime.strptime("#{m[1]} #{time}", '%Y-%m-%d %H:%M:%S')
|
32
|
+
end
|
33
|
+
|
34
|
+
def extract_title
|
35
|
+
m = /^#\+title:(.+)$/i.match(@content)
|
36
|
+
if m.nil?
|
37
|
+
# Avoid to leak absolute path
|
38
|
+
project_relative_path = @file.sub(/^#{Dir.pwd}\//, '')
|
39
|
+
return project_relative_path
|
40
|
+
end
|
41
|
+
m[1].strip
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_subtitle
|
45
|
+
m = /^#\+subtitle:(.+)$/i.match(@content)
|
46
|
+
return '' if m.nil?
|
47
|
+
m[1].strip
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_author
|
51
|
+
m = /^#\+author:(.+)$/i.match(@content)
|
52
|
+
return Neruda::Config.settings['author'] if m.nil?
|
53
|
+
m[1].strip
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_keywords
|
57
|
+
m = /^#\+keywords:(.+)$/i.match(@content)
|
58
|
+
return [] if m.nil?
|
59
|
+
m[1].split(',').map(&:strip)
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_lang
|
63
|
+
m = /^#\+language:(.+)$/i.match(@content)
|
64
|
+
return Neruda::Config.settings['lang'] if m.nil?
|
65
|
+
m[1].strip
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_excerpt
|
69
|
+
@content.scan(/^#\+description:(.+)$/i).map { |l| l[0].strip }.join(' ')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'neruda/config'
|
4
|
+
require 'neruda/emacs'
|
5
|
+
|
6
|
+
module Neruda
|
7
|
+
# This module holds HTML formatter methods for the {Neruda::OrgFile}
|
8
|
+
# class.
|
9
|
+
module OrgFileHtmlizer
|
10
|
+
# Publish the current file or the entire project if
|
11
|
+
# {Neruda::OrgFile#file @file} is ~nil~.
|
12
|
+
#
|
13
|
+
# @return [Boolean, nil] the underlying ~system~ method return value
|
14
|
+
def publish
|
15
|
+
Neruda::Emacs.new(
|
16
|
+
file_path: @file, verbose: @options[:verbose]
|
17
|
+
).publish
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Format {Neruda::OrgFile#keywords} list in an HTML listing.
|
23
|
+
#
|
24
|
+
# @return [String] the HTML keywords list
|
25
|
+
def keywords_to_html
|
26
|
+
domain = Neruda::Config.settings['domain']
|
27
|
+
klist = @keywords.map do |k|
|
28
|
+
<<~KEYWORDLINK
|
29
|
+
<li class="keyword">
|
30
|
+
<a href="#{domain}/tags/#{Neruda::OrgFile.slug(k)}.html">#{k}</a>
|
31
|
+
</li>
|
32
|
+
KEYWORDLINK
|
33
|
+
end.join
|
34
|
+
"<ul class=\"keywords-list\">#{klist}</ul>"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Format {Neruda::OrgFile#date} as a HTML `time` tag.
|
38
|
+
#
|
39
|
+
# @return [String] the HTML `time` tag
|
40
|
+
def date_to_html(dateformat = :full)
|
41
|
+
return '<time></time>' if @date.nil?
|
42
|
+
"<time datetime=\"#{@date.rfc3339}\">#{datestring(dateformat)}</time>"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Format {Neruda::OrgFile#author} in a HTML `span` tag with a
|
46
|
+
# specific class.
|
47
|
+
#
|
48
|
+
# @return [String] the author HTML `span`
|
49
|
+
def author_to_html
|
50
|
+
"<span class=\"author\">#{@author}</span>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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.dig('preview', '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 = format(
|
27
|
+
'%<path>s/index.html', path: local_path.delete_suffix('/')
|
28
|
+
)
|
29
|
+
end
|
30
|
+
return local_path if File.exist? local_path
|
31
|
+
raise WEBrick::HTTPStatus::NotFound, 'Not found.'
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_body(local_path, local_host)
|
35
|
+
body = IO.read local_path
|
36
|
+
return body unless local_path.match?(/\.(?:ht|x)ml$/)
|
37
|
+
domain = Neruda::Config.settings['domain']
|
38
|
+
return body if domain == ''
|
39
|
+
body.gsub(/"file:\/\//, format('"%<host>s', host: local_host))
|
40
|
+
.gsub(/"#{domain}/, format('"%<host>s', host: local_host))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def start_preview
|
46
|
+
# Inspired by ruby un.rb library, which allows normally to start a
|
47
|
+
# webrick server in one line: ruby -run -e httpd public_html -p 5000
|
48
|
+
port = Neruda::Config.settings.dig('preview', 'server_port') || 5000
|
49
|
+
s = WEBrick::HTTPServer.new(Port: port)
|
50
|
+
s.mount '/', Neruda::PreviewServlet
|
51
|
+
['TERM', 'QUIT', 'INT'].each { |sig| trap(sig, proc { s.shutdown }) }
|
52
|
+
s.start
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,112 @@
|
|
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
|
+
case @position
|
103
|
+
when 'before'
|
104
|
+
elem.add_previous_sibling content
|
105
|
+
when 'replace'
|
106
|
+
elem.replace content
|
107
|
+
else
|
108
|
+
elem.add_next_sibling content
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/neruda/utils.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'rainbow'
|
5
|
+
require 'net/http'
|
6
|
+
require 'r18n-core'
|
7
|
+
require 'neruda/config'
|
8
|
+
|
9
|
+
module Neruda
|
10
|
+
# Embeds usefull methods, mainly used in rake tasks.
|
11
|
+
module Utils
|
12
|
+
# @return [Hash] the possible throbber themes
|
13
|
+
THROBBER_FRAMES = {
|
14
|
+
'basic' => '-\|/',
|
15
|
+
'basicdots' => '⋯⋱⋮⋰',
|
16
|
+
'moon' => '🌑🌒🌓🌔🌕🌖🌗🌘',
|
17
|
+
'clock' => '🕛🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚',
|
18
|
+
'bricks' => '⣷⣯⣟⡿⢿⣻⣽⣾',
|
19
|
+
'points' => '·⁘∷⁛∷⁘',
|
20
|
+
'quadrant2' => '▙▛▜▟',
|
21
|
+
'default' => ['⠁ ⠂ ⠄ ⡀ ⠄ ⠂ ⠁', '⠂ ⠁ ⠂ ⠄ ⡀ ⠄ ⠂', '⠄ ⠂ ⠁ ⠂ ⠄ ⡀ ⠄',
|
22
|
+
'⡀ ⠄ ⠂ ⠁ ⠂ ⠄ ⡀', '⠄ ⡀ ⠄ ⠂ ⠁ ⠂ ⠄', '⠂ ⠄ ⡀ ⠄ ⠂ ⠁ ⠂']
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
# @return [Hash] the possible ~pablo~ options and their
|
26
|
+
# configuration
|
27
|
+
PABLO_OPTIONS = {
|
28
|
+
'-a' => { long: 'author' },
|
29
|
+
'-l' => { long: 'lang', keyword: 'LOCALE' },
|
30
|
+
'-t' => { long: 'title' },
|
31
|
+
'-p' => { long: 'path' },
|
32
|
+
'-d' => { long: 'directory', boolean: true },
|
33
|
+
'-v' => { long: 'verbose', boolean: true, meth: :on_tail },
|
34
|
+
'-h' => { long: 'help', boolean: true, meth: :on_tail },
|
35
|
+
'-V' => { long: 'version', boolean: true, meth: :on_tail }
|
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
|
+
'config' => { alias: 'init' },
|
43
|
+
'preview' => { opts: ['-h'] },
|
44
|
+
'open' => { opts: ['-a', '-l', '-t', '-d', '-p', '-v', '-h'] },
|
45
|
+
'edit' => { alias: 'open' },
|
46
|
+
'build' => { opts: ['-h'] },
|
47
|
+
'publish' => { opts: ['-h'] },
|
48
|
+
'help' => { opts: ['-h'] },
|
49
|
+
'basic' => { opts: ['-h', '-V'], label: '<command>' }
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
class << self
|
53
|
+
# Animates strings in the user console to alert him that something
|
54
|
+
# is running in the background.
|
55
|
+
#
|
56
|
+
# The animation is chosen among a bunch of themes, with the
|
57
|
+
# configuration option ~throbber~ (retrieved via
|
58
|
+
# {Neruda::Config#settings}).
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# long_stuff = Thread.new { very_long_operation }
|
62
|
+
# Neruda::Utils.throbber(long_stuff, 'Computing hard stuff:')
|
63
|
+
#
|
64
|
+
# @param thread [Thread] the long-running operation to decorate
|
65
|
+
# @param message [String] the message to display before the throbber
|
66
|
+
# @return [void]
|
67
|
+
def throbber(thread, message)
|
68
|
+
frames = select_throbber_frames
|
69
|
+
begin
|
70
|
+
run_and_decorate_thread thread, message, frames
|
71
|
+
rescue RuntimeError => e
|
72
|
+
throbber_error message
|
73
|
+
raise e
|
74
|
+
else
|
75
|
+
done = Rainbow('done'.ljust(frames[0].length)).green
|
76
|
+
puts "#{message} #{done}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the short and long options specification for a given
|
81
|
+
# short option.
|
82
|
+
#
|
83
|
+
# This method use the {Neruda::Utils::PABLO_OPTIONS} Hash to
|
84
|
+
# retrieve corresponding values.
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# spec = Neruda::Utils.decorate_option('-a')
|
88
|
+
# => ['-a AUTHOR', '--author AUTHOR']
|
89
|
+
#
|
90
|
+
# @param short [String] the short option to decorate
|
91
|
+
# @return [Array] the short and long specification for an option
|
92
|
+
def decorate_option(short)
|
93
|
+
opt = Neruda::Utils::PABLO_OPTIONS[short]
|
94
|
+
long = "--#{opt[:long]}"
|
95
|
+
return [short, long] if opt[:boolean]
|
96
|
+
key = opt[:keyword] || opt[:long].upcase
|
97
|
+
[short + key, format('%<long>s %<key>s', long: long, key: key)]
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the ~pablo~ help summary for a given command.
|
101
|
+
#
|
102
|
+
# @param command [String] the command for which a summary
|
103
|
+
# should be given
|
104
|
+
# @return [String]
|
105
|
+
def summarize_command(command)
|
106
|
+
Neruda::Utils::PABLO_COMMANDS[command][:opts].map do |k|
|
107
|
+
short, long = Neruda::Utils.decorate_option(k)
|
108
|
+
opt = Neruda::Utils::PABLO_OPTIONS[k]
|
109
|
+
label = [short, long].join(', ')
|
110
|
+
line = [format(' %<opt>s', opt: label).ljust(30)]
|
111
|
+
if R18n.t.pablo.options[opt[:long]].translated?
|
112
|
+
line << R18n.t.pablo.options[opt[:long]]
|
113
|
+
end
|
114
|
+
line.join(' ')
|
115
|
+
end.join("\n")
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns a formatted list of available commands for ~pablo~.
|
119
|
+
#
|
120
|
+
# @return [String]
|
121
|
+
def list_commands
|
122
|
+
lines = []
|
123
|
+
Neruda::Utils::PABLO_COMMANDS.each do |cmd, opt|
|
124
|
+
next if cmd == 'basic'
|
125
|
+
line = [' ', cmd.ljust(10)]
|
126
|
+
if opt.has_key? :alias
|
127
|
+
line << R18n.t.pablo.commands.alias(opt[:alias])
|
128
|
+
else
|
129
|
+
line << R18n.t.pablo.commands[cmd]
|
130
|
+
end
|
131
|
+
lines << line.join(' ')
|
132
|
+
end
|
133
|
+
lines.join("\n")
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the real command name for a given command, which may be
|
137
|
+
# an alias.
|
138
|
+
#
|
139
|
+
# @param command [String] the command to resolve
|
140
|
+
# @return [String]
|
141
|
+
def resolve_possible_alias(command)
|
142
|
+
return 'basic' unless Neruda::Utils::PABLO_COMMANDS.include?(command)
|
143
|
+
cmd_opt = Neruda::Utils::PABLO_COMMANDS[command]
|
144
|
+
return cmd_opt[:alias] if cmd_opt.has_key?(:alias)
|
145
|
+
command
|
146
|
+
end
|
147
|
+
|
148
|
+
# Try to discover the current host operating system.
|
149
|
+
#
|
150
|
+
# @return [String] either apple, windows or linux (default)
|
151
|
+
# :nocov:
|
152
|
+
def current_os
|
153
|
+
if ENV['OS'] == 'Windows_NT' || RUBY_PLATFORM =~ /cygwin/
|
154
|
+
return 'windows'
|
155
|
+
end
|
156
|
+
return 'apple' if RUBY_PLATFORM =~ /darwin/
|
157
|
+
'linux'
|
158
|
+
end
|
159
|
+
# :nocov:
|
160
|
+
|
161
|
+
# Download latest org-mode tarball.
|
162
|
+
#
|
163
|
+
# @return [String] the downloaded org-mode version
|
164
|
+
def download_org
|
165
|
+
# :nocov:
|
166
|
+
return if Neruda::Config.org_last_version.nil?
|
167
|
+
# :nocov:
|
168
|
+
tarball = "org-#{Neruda::Config.org_last_version}.tar.gz"
|
169
|
+
dest_file = "tmp/#{tarball}"
|
170
|
+
return if File.exist?(dest_file)
|
171
|
+
uri = URI("https://orgmode.org/#{tarball}")
|
172
|
+
# Will crash on purpose if anything goes wrong
|
173
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
|
174
|
+
http.request(Net::HTTP::Get.new(uri)) do |response|
|
175
|
+
File.open(dest_file, 'w') do |io|
|
176
|
+
response.read_body { |chunk| io.write chunk }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def throbber_error(message)
|
185
|
+
warn(
|
186
|
+
format(
|
187
|
+
"%<message>s %<label>s\n%<explanation>s",
|
188
|
+
message: message,
|
189
|
+
label: Rainbow(R18n.t.neruda.error.label).bold.red,
|
190
|
+
explanation: Rainbow(R18n.t.neruda.error.explanation).bold
|
191
|
+
)
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
def select_throbber_frames
|
196
|
+
model = Neruda::Config.settings['throbber'] || 'default'
|
197
|
+
model = 'default' unless Neruda::Utils::THROBBER_FRAMES.has_key?(model)
|
198
|
+
Neruda::Utils::THROBBER_FRAMES[model]
|
199
|
+
end
|
200
|
+
|
201
|
+
def run_and_decorate_thread(thread, message, frames)
|
202
|
+
thread.abort_on_exception = true
|
203
|
+
current = 0
|
204
|
+
while thread.alive?
|
205
|
+
sleep 0.1
|
206
|
+
print "#{message} #{frames[current % frames.length]}\r"
|
207
|
+
current += 1
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|