fronde 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +14 -0
- data/bin/fronde +45 -0
- data/lib/fronde/cli.rb +60 -0
- data/lib/fronde/cli/commands.rb +115 -0
- data/lib/fronde/config.rb +137 -0
- data/lib/fronde/config/lisp_config.rb +269 -0
- data/lib/fronde/config/org-config.el +17 -0
- data/lib/fronde/config/ox-fronde.el +114 -0
- data/lib/fronde/emacs.rb +37 -0
- data/lib/fronde/index.rb +130 -0
- data/lib/fronde/index/atom_generator.rb +86 -0
- data/lib/fronde/index/org_generator.rb +114 -0
- data/lib/fronde/org_file.rb +301 -0
- data/lib/fronde/org_file/class_methods.rb +72 -0
- data/lib/fronde/org_file/extracter.rb +72 -0
- data/lib/fronde/org_file/htmlizer.rb +43 -0
- data/lib/fronde/preview.rb +55 -0
- data/lib/fronde/templater.rb +118 -0
- data/lib/fronde/utils.rb +214 -0
- data/lib/fronde/version.rb +6 -0
- data/lib/tasks/org.rake +96 -0
- data/lib/tasks/site.rake +55 -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
- metadata +267 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c646347a1cdbe6537384936ec392c1848617524fd7557f3c32566adb14e3c357
|
4
|
+
data.tar.gz: 41785e0d542baa6ac44c8e2f7b8d12d26db6ea5a5e10349ea4002d71b25f435d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ecac97edd7c48c3cd808c906195b11e941f84db6254f0548af1d951e8bba215a6545616cc8b425977b5453b158f5b3032e08d4cf702b1087f110054770e3c739
|
7
|
+
data.tar.gz: adb5628d1999d54284ad38c614ca29bd76b287124fb4ebc434a465225f3c1fb97acf1cce4e844de05aaf66b35c40be8216b9045535dfb05370c77eecb0cb4bde
|
data/LICENSE
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
14
|
+
|
data/bin/fronde
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'r18n-core'
|
6
|
+
require 'fronde/utils'
|
7
|
+
require 'fronde/version'
|
8
|
+
require 'fronde/cli'
|
9
|
+
|
10
|
+
R18n.default_places = File.expand_path('../locales', __dir__)
|
11
|
+
R18n.set Fronde::Config.settings['lang']
|
12
|
+
|
13
|
+
optparser = OptionParser.new
|
14
|
+
optparser.version = Fronde::VERSION
|
15
|
+
|
16
|
+
Fronde::Utils::FRONDE_OPTIONS.each do |k, opt|
|
17
|
+
optparser.send(opt[:meth] || :on, *Fronde::Utils.decorate_option(k))
|
18
|
+
end
|
19
|
+
|
20
|
+
params = {}
|
21
|
+
optparser.parse!(into: params)
|
22
|
+
|
23
|
+
if params[:version]
|
24
|
+
warn optparser.ver
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
if ARGV[0] == 'help'
|
29
|
+
params[:help] = true
|
30
|
+
ARGV.shift
|
31
|
+
end
|
32
|
+
fronde = Fronde::CLI.new(params)
|
33
|
+
command = "fronde_#{ARGV[0]}".to_sym
|
34
|
+
cmd_err = !fronde.respond_to?(command)
|
35
|
+
if params[:help] || cmd_err
|
36
|
+
cmd_err = false if params[:help] && !ARGV[0]
|
37
|
+
fronde.fronde_help(ARGV[0], error: cmd_err)
|
38
|
+
end
|
39
|
+
ARGV.shift
|
40
|
+
|
41
|
+
init_cmds = [:fronde_init, :fronde_config]
|
42
|
+
unless File.exist?('config.yml') || init_cmds.include?(command)
|
43
|
+
fronde.fronde_init
|
44
|
+
end
|
45
|
+
fronde.send command
|
data/lib/fronde/cli.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'fronde/cli/commands'
|
5
|
+
|
6
|
+
module Fronde
|
7
|
+
# Fronde CLI app
|
8
|
+
class CLI
|
9
|
+
def initialize(opts = {})
|
10
|
+
@options = { verbose: false }.merge(opts)
|
11
|
+
init_required_files
|
12
|
+
init_rake
|
13
|
+
end
|
14
|
+
|
15
|
+
include Fronde::CLICommands
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def init_required_files
|
20
|
+
init_rakefile unless File.exist?('Rakefile')
|
21
|
+
init_gitignore unless File.exist?('.gitignore')
|
22
|
+
end
|
23
|
+
|
24
|
+
def init_rake
|
25
|
+
@rake = Rake.application
|
26
|
+
Rake.verbose(false) unless @options[:verbose]
|
27
|
+
@rake.raw_load_rakefile
|
28
|
+
end
|
29
|
+
|
30
|
+
def init_rakefile
|
31
|
+
rakefile = <<~RAKE
|
32
|
+
# frozen_string_literal: true
|
33
|
+
|
34
|
+
require 'fronde/config'
|
35
|
+
require 'r18n-core'
|
36
|
+
|
37
|
+
fronde_spec = Gem::Specification.find_by_name 'fronde'
|
38
|
+
R18n.default_places = "\#{fronde_spec.gem_dir}/locales"
|
39
|
+
R18n.set(Fronde::Config.settings['lang'] || 'en')
|
40
|
+
R18n::Filters.on(:named_variables)
|
41
|
+
|
42
|
+
Dir.glob("\#{fronde_spec.gem_dir}/lib/tasks/*.rake").each { |r| import r }
|
43
|
+
|
44
|
+
task default: 'site:build'
|
45
|
+
RAKE
|
46
|
+
IO.write 'Rakefile', rakefile
|
47
|
+
end
|
48
|
+
|
49
|
+
def init_gitignore
|
50
|
+
gitignore = <<~GITIGNORE
|
51
|
+
.dir-locals.el
|
52
|
+
Rakefile
|
53
|
+
lib
|
54
|
+
public_html
|
55
|
+
var
|
56
|
+
GITIGNORE
|
57
|
+
IO.write '.gitignore', gitignore
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fronde
|
4
|
+
# Fronde commands
|
5
|
+
module CLICommands
|
6
|
+
def fronde_init
|
7
|
+
cnf = @options.merge
|
8
|
+
cnf.delete(:verbose)
|
9
|
+
cnf.transform_keys!(&:to_s)
|
10
|
+
Fronde::Config.save(Fronde::Config.settings.merge(cnf))
|
11
|
+
@rake.options.build_all = true
|
12
|
+
@rake.invoke_task('org:install')
|
13
|
+
return if File.exist? 'src/index.org'
|
14
|
+
Fronde::OrgFile.new('src/index.org', @options).write
|
15
|
+
fronde_open 'src/index.org'
|
16
|
+
end
|
17
|
+
alias_method :fronde_config, :fronde_init
|
18
|
+
|
19
|
+
def fronde_build
|
20
|
+
@rake.options.build_all = true
|
21
|
+
task = 'site:build'
|
22
|
+
task = "#{task}[true]" if @options[:force]
|
23
|
+
@rake.invoke_task task
|
24
|
+
end
|
25
|
+
|
26
|
+
def fronde_preview
|
27
|
+
Thread.new do
|
28
|
+
sleep 1
|
29
|
+
port = Fronde::Config.settings.dig('preview', 'server_port') || 5000
|
30
|
+
uri = "http://127.0.0.1:#{port}/"
|
31
|
+
current_os = Fronde::Utils.current_os
|
32
|
+
case current_os
|
33
|
+
when 'windows'
|
34
|
+
system 'start', uri
|
35
|
+
when 'apple'
|
36
|
+
system 'open', uri
|
37
|
+
else
|
38
|
+
system 'gio', 'open', uri
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@rake.invoke_task('site:preview')
|
42
|
+
end
|
43
|
+
|
44
|
+
def fronde_open(file_path = ARGV[0])
|
45
|
+
editor = ENV['EDITOR'] || ENV['VISUAL'] || 'emacs'
|
46
|
+
cmd = [editor]
|
47
|
+
if file_path.nil? || !File.file?(file_path)
|
48
|
+
# file_path may be updated with title given in options
|
49
|
+
file_path = create_new_file(file_path)
|
50
|
+
# Only move to the end of file for new file. Let the editor handle
|
51
|
+
# the best position for already existing files
|
52
|
+
cmd << '+6'
|
53
|
+
end
|
54
|
+
cmd << file_path
|
55
|
+
system(*cmd)
|
56
|
+
end
|
57
|
+
alias_method :fronde_edit, :fronde_open
|
58
|
+
|
59
|
+
def fronde_publish
|
60
|
+
@rake.invoke_task('sync:push')
|
61
|
+
end
|
62
|
+
|
63
|
+
def fronde_help(command = 'basic', error: false)
|
64
|
+
warn R18n.t.fronde.bin.error.no_command if error
|
65
|
+
cmd = Fronde::Utils.resolve_possible_alias(command)
|
66
|
+
cmd_opt = Fronde::Utils::FRONDE_COMMANDS[cmd]
|
67
|
+
label = cmd_opt[:label] || command
|
68
|
+
warn format("%<label>s\n\n", label: R18n.t.fronde.bin.usage(label))
|
69
|
+
if R18n.t.fronde.bin.commands[cmd].translated?
|
70
|
+
warn format("%<label>s\n\n", label: R18n.t.fronde.bin.commands[cmd])
|
71
|
+
end
|
72
|
+
warn help_command_body(cmd).join("\n")
|
73
|
+
exit 1 if error
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def new_file_name(file_path)
|
80
|
+
file_path = File.expand_path(file_path || '')
|
81
|
+
return file_path if file_path[-4..] == '.org'
|
82
|
+
# file_path seems to be a dir path. Thus we have to create the new
|
83
|
+
# filename from its title
|
84
|
+
title = @options[:title]
|
85
|
+
# No title, nor a reliable file_path? Better abort
|
86
|
+
return nil if title.nil? || title == ''
|
87
|
+
filename = "#{Fronde::OrgFile.slug(title)}.org"
|
88
|
+
File.join file_path, filename
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_new_file(file_path)
|
92
|
+
filename = new_file_name(file_path)
|
93
|
+
if filename.nil?
|
94
|
+
warn R18n.t.fronde.bin.error.no_file
|
95
|
+
exit 1
|
96
|
+
end
|
97
|
+
FileUtils.mkdir_p File.dirname(filename)
|
98
|
+
Fronde::OrgFile.new(filename, @options).write
|
99
|
+
filename
|
100
|
+
end
|
101
|
+
|
102
|
+
def help_command_body(command)
|
103
|
+
body = [
|
104
|
+
R18n.t.fronde.bin.options.cmd_title,
|
105
|
+
Fronde::Utils.summarize_command(command)
|
106
|
+
]
|
107
|
+
return body unless command == 'basic'
|
108
|
+
body + [
|
109
|
+
'',
|
110
|
+
R18n.t.fronde.bin.commands.cmd_title,
|
111
|
+
Fronde::Utils.list_commands
|
112
|
+
]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'fronde/config/lisp_config'
|
5
|
+
|
6
|
+
module Fronde
|
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
|
+
# Fronde::Config.settings['author']
|
28
|
+
# => "Alice Doe"
|
29
|
+
# #+end_src
|
30
|
+
class Config
|
31
|
+
extend Fronde::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:Fronde/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 fronde 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:Fronde/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
|
@@ -0,0 +1,269 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'fronde/version'
|
6
|
+
|
7
|
+
module Fronde
|
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?('var/tmp/last_org_version')
|
17
|
+
@org_version = IO.read('var/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 'var/tmp'
|
25
|
+
IO.write('var/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__', Fronde::VERSION)
|
42
|
+
.gsub('__WORK_DIR__', workdir)
|
43
|
+
.gsub('__FRONDE_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
|
+
FileUtils.mkdir_p "#{workdir}/var/lib"
|
52
|
+
IO.write("#{workdir}/var/lib/org-config.el", content)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Generate emacs directory variables file.
|
56
|
+
#
|
57
|
+
# This method generate the file ~.dir-locals.el~, which is
|
58
|
+
# responsible to load fronde Org settings when visiting an Org file
|
59
|
+
# of this fronde instance.
|
60
|
+
#
|
61
|
+
# @return [Integer] the length written (as returned by the
|
62
|
+
# underlying ~IO.write~ method call)
|
63
|
+
def write_dir_locals
|
64
|
+
workdir = Dir.pwd
|
65
|
+
# rubocop:disable Layout/LineLength
|
66
|
+
IO.write(
|
67
|
+
"#{workdir}/.dir-locals.el",
|
68
|
+
"((org-mode . ((eval . (load-file \"#{workdir}/var/lib/org-config.el\")))))"
|
69
|
+
)
|
70
|
+
# rubocop:enable Layout/LineLength
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def r18n_full_datetime_format
|
76
|
+
locale = R18n.get.locale
|
77
|
+
date_fmt = R18n.t.fronde.index.full_date_format(
|
78
|
+
date: locale.full_format
|
79
|
+
)
|
80
|
+
date_fmt = locale.year_format.sub('_', date_fmt)
|
81
|
+
time_fmt = locale.time_format.delete('_').strip
|
82
|
+
R18n.t.fronde.index.full_date_with_time_format(
|
83
|
+
date: date_fmt, time: time_fmt
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def ruby_to_lisp_boolean(value)
|
88
|
+
return 't' if value == true
|
89
|
+
'nil'
|
90
|
+
end
|
91
|
+
|
92
|
+
def project_names(projects)
|
93
|
+
names = projects.keys.map do |p|
|
94
|
+
["\"#{p}\"", "\"#{p}-assets\""]
|
95
|
+
end.flatten
|
96
|
+
unless settings['theme'] == 'default'
|
97
|
+
names << "\"theme-#{settings['theme']}\""
|
98
|
+
end
|
99
|
+
sources.each do |s|
|
100
|
+
# Default theme defined in settings is already included
|
101
|
+
next unless s['theme'] && s['theme'] != settings['theme']
|
102
|
+
# Never include theme named 'default' as it does not rely on any
|
103
|
+
# file to export.
|
104
|
+
next if s['theme'] == 'default'
|
105
|
+
theme = "\"theme-#{s['theme']}\""
|
106
|
+
next if names.include? theme
|
107
|
+
names << theme
|
108
|
+
end
|
109
|
+
names.join(' ')
|
110
|
+
end
|
111
|
+
|
112
|
+
def all_projects(projects)
|
113
|
+
projects.values.join("\n").strip
|
114
|
+
.gsub(/\n\s*\n/, "\n")
|
115
|
+
.gsub(/\n/, "\n ")
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return the full path to the publication path of a given project
|
119
|
+
# configuration.
|
120
|
+
#
|
121
|
+
# @param project [Hash] a project configuration (as extracted from
|
122
|
+
# the ~sources~ key)
|
123
|
+
# @return [String] the full path to the target dir of this project
|
124
|
+
def publication_path(project)
|
125
|
+
publish_in = [Dir.pwd, settings['public_folder']]
|
126
|
+
publish_in << project['target'] unless project['target'] == '.'
|
127
|
+
publish_in.join('/')
|
128
|
+
end
|
129
|
+
|
130
|
+
def org_project(project_name, opts)
|
131
|
+
publish_in = publication_path(opts)
|
132
|
+
other_lines = [
|
133
|
+
format(':recursive %<value>s',
|
134
|
+
value: ruby_to_lisp_boolean(opts['recursive']))
|
135
|
+
]
|
136
|
+
if opts['exclude']
|
137
|
+
other_lines << format(':exclude "%<value>s"',
|
138
|
+
value: opts['exclude'])
|
139
|
+
end
|
140
|
+
themeconf = org_theme_config(opts['theme'])
|
141
|
+
<<~ORGPROJECT
|
142
|
+
("#{project_name}"
|
143
|
+
:base-directory "#{opts['path']}"
|
144
|
+
:base-extension "org"
|
145
|
+
#{other_lines.join("\n ")}
|
146
|
+
:publishing-directory "#{publish_in}"
|
147
|
+
:publishing-function org-html-publish-to-html
|
148
|
+
#{opts['org_headers']})
|
149
|
+
("#{project_name}-assets"
|
150
|
+
:base-directory "#{opts['path']}"
|
151
|
+
:base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|svg\\\\\\|pdf"
|
152
|
+
#{other_lines[0]}
|
153
|
+
:publishing-directory "#{publish_in}"
|
154
|
+
:publishing-function org-publish-attachment)
|
155
|
+
#{themeconf}
|
156
|
+
ORGPROJECT
|
157
|
+
end
|
158
|
+
|
159
|
+
def org_default_postamble
|
160
|
+
<<~POSTAMBLE
|
161
|
+
<p><span class="author">#{R18n.t.fronde.org.postamble.written_by}</span>
|
162
|
+
#{R18n.t.fronde.org.postamble.with_emacs}</p>
|
163
|
+
<p class="date">#{R18n.t.fronde.org.postamble.last_modification}</p>
|
164
|
+
<p class="validation">%v</p>
|
165
|
+
POSTAMBLE
|
166
|
+
end
|
167
|
+
|
168
|
+
def org_default_html_head
|
169
|
+
<<~HTMLHEAD
|
170
|
+
<link rel="stylesheet" type="text/css" media="screen"
|
171
|
+
href="__DOMAIN__/assets/__THEME__/css/style.css">
|
172
|
+
<link rel="stylesheet" type="text/css" media="screen"
|
173
|
+
href="__DOMAIN__/assets/__THEME__/css/htmlize.css">
|
174
|
+
__ATOM_FEED__
|
175
|
+
HTMLHEAD
|
176
|
+
end
|
177
|
+
|
178
|
+
def org_default_html_options(project)
|
179
|
+
defaults = {
|
180
|
+
'section-numbers' => 'nil',
|
181
|
+
'with-toc' => 'nil',
|
182
|
+
'html-postamble' => org_default_postamble,
|
183
|
+
'html-head' => '__ATOM_FEED__',
|
184
|
+
'html-head-include-default-style' => 't',
|
185
|
+
'html-head-include-scripts' => 't'
|
186
|
+
}
|
187
|
+
curtheme = project['theme'] || settings['theme']
|
188
|
+
return defaults if curtheme.nil? || curtheme == 'default'
|
189
|
+
defaults['html-head'] = org_default_html_head
|
190
|
+
defaults['html-head-include-default-style'] = 'nil'
|
191
|
+
defaults['html-head-include-scripts'] = 'nil'
|
192
|
+
defaults
|
193
|
+
end
|
194
|
+
|
195
|
+
def expand_vars_in_html_head(head, project)
|
196
|
+
curtheme = project['theme'] || settings['theme']
|
197
|
+
# Head may be frozen when coming from settings
|
198
|
+
head = head.gsub('__THEME__', curtheme)
|
199
|
+
.gsub('__DOMAIN__', settings['domain'])
|
200
|
+
return head.gsub('__ATOM_FEED__', '') unless project['is_blog']
|
201
|
+
atomfeed = <<~ATOMFEED
|
202
|
+
<link rel="alternate" type="application/atom+xml" title="Atom 1.0"
|
203
|
+
href="#{settings['domain']}/feeds/index.xml" />
|
204
|
+
ATOMFEED
|
205
|
+
head.gsub('__ATOM_FEED__', atomfeed)
|
206
|
+
end
|
207
|
+
|
208
|
+
def cast_lisp_value(value)
|
209
|
+
return 't' if value.is_a?(TrueClass)
|
210
|
+
return 'nil' if value.nil? || value.is_a?(FalseClass)
|
211
|
+
value.strip.gsub(/"/, '\"')
|
212
|
+
end
|
213
|
+
|
214
|
+
def build_project_org_headers(project)
|
215
|
+
orgtplopts = org_default_html_options(project).merge(
|
216
|
+
settings['org-html'] || {}, project['org-html'] || {}
|
217
|
+
)
|
218
|
+
orgtpl = []
|
219
|
+
lisp_keywords = ['t', 'nil', '1', '-1', '0'].freeze
|
220
|
+
orgtplopts.each do |k, v|
|
221
|
+
v = expand_vars_in_html_head(v, project) if k == 'html-head'
|
222
|
+
val = cast_lisp_value(v)
|
223
|
+
if lisp_keywords.include? val
|
224
|
+
orgtpl << ":#{k} #{val}"
|
225
|
+
else
|
226
|
+
orgtpl << ":#{k} \"#{val}\""
|
227
|
+
end
|
228
|
+
end
|
229
|
+
orgtpl.join("\n ")
|
230
|
+
end
|
231
|
+
|
232
|
+
def org_generate_projects(with_tags: false)
|
233
|
+
projects = {}
|
234
|
+
projects_sources = sources
|
235
|
+
if with_tags
|
236
|
+
tags_conf = build_source('tags')
|
237
|
+
tags_conf['recursive'] = false
|
238
|
+
projects_sources << tags_conf
|
239
|
+
end
|
240
|
+
projects_sources.each do |opts|
|
241
|
+
opts['org_headers'] = build_project_org_headers(opts)
|
242
|
+
projects[opts['name']] = org_project(opts['name'], opts)
|
243
|
+
end
|
244
|
+
projects
|
245
|
+
end
|
246
|
+
|
247
|
+
def org_default_theme_config
|
248
|
+
theme_config = org_theme_config(settings['theme'])
|
249
|
+
return theme_config if theme_config == ''
|
250
|
+
output = theme_config.split("\n").map do |line|
|
251
|
+
" #{line}"
|
252
|
+
end
|
253
|
+
format("\n%<conf>s", conf: output.join("\n"))
|
254
|
+
end
|
255
|
+
|
256
|
+
def org_theme_config(theme)
|
257
|
+
return '' if theme.nil? || theme == 'default'
|
258
|
+
workdir = Dir.pwd
|
259
|
+
<<~THEMECONFIG
|
260
|
+
("theme-#{theme}"
|
261
|
+
:base-directory "#{workdir}/themes/#{theme}"
|
262
|
+
:base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|js\\\\\\|css\\\\\\|otf\\\\\\|ttf\\\\\\|woff2?"
|
263
|
+
:recursive t
|
264
|
+
:publishing-directory "#{workdir}/#{settings['public_folder']}/assets/#{theme}"
|
265
|
+
:publishing-function org-publish-attachment)
|
266
|
+
THEMECONFIG
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|