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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/bin/pablo +105 -246
  3. data/lib/neruda/config.rb +94 -0
  4. data/lib/neruda/config/lisp_config.rb +201 -0
  5. data/lib/neruda/config/org-config.el +17 -0
  6. data/lib/neruda/config/ox-neruda.el +88 -0
  7. data/lib/neruda/index.rb +108 -0
  8. data/lib/neruda/index/atom_generator.rb +86 -0
  9. data/lib/neruda/index/org_generator.rb +92 -0
  10. data/lib/neruda/org_file.rb +266 -0
  11. data/lib/neruda/org_file/class_methods.rb +55 -0
  12. data/lib/neruda/org_file/extracter.rb +61 -0
  13. data/lib/neruda/org_file/htmlizer.rb +78 -0
  14. data/lib/neruda/preview.rb +53 -0
  15. data/lib/neruda/templater.rb +111 -0
  16. data/lib/neruda/utils.rb +130 -0
  17. data/lib/neruda/version.rb +5 -0
  18. data/lib/tasks/org.rake +69 -0
  19. data/lib/tasks/site.rake +84 -0
  20. data/lib/tasks/sync.rake +30 -0
  21. data/locales/en.yml +18 -0
  22. data/locales/fr.yml +18 -0
  23. data/themes/default/css/style.css +216 -0
  24. data/themes/default/fonts/Yanone_Kaffeesatz_400.woff +0 -0
  25. data/themes/default/fonts/Yanone_Kaffeesatz_400.woff2 +0 -0
  26. metadata +145 -39
  27. data/README.md +0 -98
  28. data/docs/Rakefile.example +0 -4
  29. data/docs/config.yml.example +0 -17
  30. data/lib/assets/chapter.slim +0 -14
  31. data/lib/assets/index.slim +0 -13
  32. data/lib/assets/layout.slim +0 -17
  33. data/lib/assets/style.css +0 -199
  34. data/lib/neruda.rb +0 -112
  35. data/lib/neruda/chapter.rb +0 -26
  36. data/lib/neruda/url.rb +0 -14
  37. data/lib/tasks/book.rake +0 -60
  38. data/lib/tasks/capistrano/chapters.rake +0 -60
  39. data/lib/tasks/capistrano/sinatra.rake +0 -18
  40. data/lib/tasks/chapters.rake +0 -132
  41. 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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neruda
4
+ VERSION = '0.1.0'
5
+ end
@@ -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