fronde 0.3.4 → 0.4.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 +4 -4
- data/bin/fronde +15 -30
- data/lib/ext/nil_time.rb +25 -0
- data/lib/ext/r18n.rb +17 -0
- data/lib/ext/time.rb +49 -0
- data/lib/fronde/cli/commands.rb +92 -103
- data/lib/fronde/cli/data/Rakefile +8 -0
- data/lib/fronde/cli/data/config.yml +13 -0
- data/lib/fronde/cli/data/gitignore +7 -0
- data/lib/fronde/cli/data/zsh_completion +37 -0
- data/lib/fronde/cli/helpers.rb +55 -0
- data/lib/fronde/cli/opt_parse.rb +143 -0
- data/lib/fronde/cli/throbber.rb +99 -0
- data/lib/fronde/cli.rb +41 -42
- data/lib/fronde/config/data/org-config.el +24 -0
- data/lib/fronde/config/{ox-fronde.el → data/ox-fronde.el} +1 -1
- data/lib/fronde/config/helpers.rb +80 -0
- data/lib/fronde/config/lisp.rb +70 -0
- data/lib/fronde/config.rb +135 -99
- data/lib/fronde/emacs.rb +23 -20
- data/lib/fronde/index/atom_generator.rb +55 -66
- data/lib/fronde/index/data/all_tags.org +14 -0
- data/lib/fronde/index/data/template.org +22 -0
- data/lib/fronde/index/data/template.xml +37 -0
- data/lib/fronde/index/org_generator.rb +70 -88
- data/lib/fronde/index.rb +56 -82
- data/lib/fronde/org/file.rb +287 -0
- data/lib/fronde/org/file_extracter.rb +98 -0
- data/lib/fronde/org.rb +103 -0
- data/lib/fronde/preview.rb +43 -39
- data/lib/fronde/slug.rb +27 -0
- data/lib/fronde/source/gemini.rb +39 -0
- data/lib/fronde/source/html.rb +67 -0
- data/lib/fronde/source.rb +204 -0
- data/lib/fronde/templater.rb +94 -71
- data/lib/fronde/version.rb +1 -1
- data/lib/tasks/cli.rake +33 -0
- data/lib/tasks/org.rake +62 -42
- data/lib/tasks/site.rake +68 -30
- data/lib/tasks/sync.rake +41 -21
- data/lib/tasks/tags.rake +11 -7
- data/locales/en.yml +60 -14
- data/locales/fr.yml +68 -14
- metadata +53 -110
- data/lib/fronde/config/lisp_config.rb +0 -340
- data/lib/fronde/config/org-config.el +0 -19
- data/lib/fronde/org_file/class_methods.rb +0 -72
- data/lib/fronde/org_file/extracter.rb +0 -72
- data/lib/fronde/org_file/htmlizer.rb +0 -43
- data/lib/fronde/org_file.rb +0 -298
- data/lib/fronde/utils.rb +0 -229
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rainbow'
|
4
|
+
|
5
|
+
module Fronde
|
6
|
+
module CLI
|
7
|
+
# Decorations for the command line
|
8
|
+
class Throbber
|
9
|
+
# @return [Hash] the possible throbber themes
|
10
|
+
THROBBER_FRAMES = {
|
11
|
+
'basic' => '-\|/',
|
12
|
+
'basicdots' => '⋯⋱⋮⋰',
|
13
|
+
'moon' => '🌑🌒🌓🌔🌕🌖🌗🌘',
|
14
|
+
'clock' => '🕛🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚',
|
15
|
+
'bricks' => '⣷⣯⣟⡿⢿⣻⣽⣾',
|
16
|
+
'points' => '·⁘⁛⁘',
|
17
|
+
'quadrant' => '▙▛▜▟',
|
18
|
+
'default' => ['⠁ ⠂ ⠄ ⡀ ⠄ ⠂ ⠁', '⠂ ⠁ ⠂ ⠄ ⡀ ⠄ ⠂', '⠄ ⠂ ⠁ ⠂ ⠄ ⡀ ⠄',
|
19
|
+
'⡀ ⠄ ⠂ ⠁ ⠂ ⠄ ⡀', '⠄ ⡀ ⠄ ⠂ ⠁ ⠂ ⠄', '⠂ ⠄ ⡀ ⠄ ⠂ ⠁ ⠂']
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def initialize(thread, message)
|
23
|
+
@frames = select_frames
|
24
|
+
@thread = thread
|
25
|
+
@thread.abort_on_exception = false
|
26
|
+
@thread.report_on_exception = false
|
27
|
+
@message = message
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
term_width = terminal_width
|
32
|
+
frames_len = @frames.length
|
33
|
+
current = 0
|
34
|
+
while @thread.alive?
|
35
|
+
sleep 0.1
|
36
|
+
frame = @frames[current % frames_len]
|
37
|
+
message = "#{@message} #{frame}"
|
38
|
+
print "#{message.ljust(term_width)}\r"
|
39
|
+
current += 1
|
40
|
+
end
|
41
|
+
@thread.join # Ensure any inner exception is re-raised
|
42
|
+
rescue RuntimeError => e
|
43
|
+
show_error
|
44
|
+
raise e
|
45
|
+
else
|
46
|
+
done = Rainbow(R18n.t.fronde.bin.done).green
|
47
|
+
puts "#{@message} #{done}".ljust(term_width)
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
# Animates strings in the user console to alert him that something
|
52
|
+
# is running in the background.
|
53
|
+
#
|
54
|
+
# The animation is chosen among a bunch of themes, with the
|
55
|
+
# configuration option ~throbber~ (retrieved via
|
56
|
+
# {Fronde::Config::Store#get}).
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# long_stuff = Thread.new { very_long_operation }
|
60
|
+
# Fronde::CLI::Throbber.run(long_stuff, 'Computing hard stuff:')
|
61
|
+
#
|
62
|
+
# @param thread [Thread] the long-running operation to decorate
|
63
|
+
# @param message [String] the message to display before the throbber
|
64
|
+
# @return [void]
|
65
|
+
def run(thread, message)
|
66
|
+
throbber = new(thread, message)
|
67
|
+
throbber.run
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def terminal_width
|
74
|
+
# Not a tty. Docker?
|
75
|
+
return 0 unless system('test -t 0')
|
76
|
+
|
77
|
+
`stty size`.strip.split[1].to_i - 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def show_error
|
81
|
+
warn(
|
82
|
+
format(
|
83
|
+
"%<message>s %<label>s\n%<explanation>s",
|
84
|
+
message: @message,
|
85
|
+
label: Rainbow(R18n.t.fronde.error.bin.label).bold.red,
|
86
|
+
explanation: Rainbow(R18n.t.fronde.error.bin.explanation).bold
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def select_frames
|
92
|
+
model = Fronde::CONFIG.get 'throbber', 'default'
|
93
|
+
model = 'default' unless THROBBER_FRAMES.has_key?(model)
|
94
|
+
|
95
|
+
THROBBER_FRAMES[model]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/fronde/cli.rb
CHANGED
@@ -1,60 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rake'
|
4
|
-
|
4
|
+
require_relative 'cli/commands'
|
5
5
|
|
6
6
|
module Fronde
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
module CLI
|
8
|
+
# Fronde CLI app
|
9
|
+
class App
|
10
|
+
def initialize(opts = {})
|
11
|
+
@options = { verbose: false }.merge(opts)
|
12
|
+
@command = @rake = nil
|
13
|
+
@argv = []
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
+
include Commands
|
16
17
|
|
17
|
-
|
18
|
+
def run(argv)
|
19
|
+
@argv = argv
|
20
|
+
@command = OptParse.resolve_possible_alias(@argv.shift || 'basic')
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
if help_param_given?
|
23
|
+
return 2 if @options[:recover_from_error]
|
24
|
+
return 0
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
@rake = Rake.application
|
26
|
-
Rake.verbose(false) unless @options[:verbose]
|
27
|
-
@rake.raw_load_rakefile
|
28
|
-
end
|
27
|
+
init_rake if %w[build preview publish].include?(@command)
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
# frozen_string_literal: true
|
29
|
+
method = "fronde_#{@command}".to_sym
|
30
|
+
return 2 if method_unknown?(method)
|
33
31
|
|
34
|
-
|
35
|
-
|
32
|
+
send method
|
33
|
+
end
|
36
34
|
|
37
|
-
|
38
|
-
R18n.default_places = "\#{fronde_spec.gem_dir}/locales"
|
39
|
-
R18n.set(Fronde::Config.get('lang', 'en'))
|
40
|
-
R18n::Filters.on(:named_variables)
|
35
|
+
private
|
41
36
|
|
42
|
-
|
37
|
+
def init_rake
|
38
|
+
@rake = Rake.application
|
39
|
+
Rake.verbose(false) unless @options[:verbose]
|
40
|
+
@rake.load_rakefile
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
43
|
+
def help_param_given?
|
44
|
+
return false unless @options[:help]
|
45
|
+
|
46
|
+
fronde_help
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_unknown?(method)
|
51
|
+
return false if respond_to?(method)
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
lib
|
54
|
-
public_html
|
55
|
-
var
|
56
|
-
GITIGNORE
|
57
|
-
File.write '.gitignore', gitignore
|
53
|
+
warn R18n.t.fronde.error.bin.no_command
|
54
|
+
fronde_help
|
55
|
+
true
|
56
|
+
end
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
;; Add org-mode to load path
|
2
|
+
(add-to-list 'load-path (expand-file-name "org-{{ org_version }}/lisp" "{{ work_dir }}/lib"))
|
3
|
+
;; Load last version of htmlize.el
|
4
|
+
(load-file (expand-file-name "htmlize.el" "{{ work_dir }}/lib"))
|
5
|
+
|
6
|
+
;; Current project options
|
7
|
+
(setq fronde/version "{{ version }}"
|
8
|
+
fronde/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-gmi-timestamp-format "{{ long_date_fmt }}"
|
13
|
+
org-publish-project-alist
|
14
|
+
`({% for project in all_projects -%}
|
15
|
+
("{{ project.name }}"
|
16
|
+
{%- for attribute in project.attributes %}
|
17
|
+
:{{ attribute[0] }} {{ attribute[1] | cast_lisp_value: attribute[0] }}
|
18
|
+
{%- endfor %})
|
19
|
+
{% endfor -%}
|
20
|
+
("website" :components ("{{ all_projects | map: 'name' | join: '" "' | remove: '" "tags' }}"))))
|
21
|
+
|
22
|
+
;; Load fronde lib
|
23
|
+
(load-file (expand-file-name "ox-gmi.el" "{{ work_dir }}/lib"))
|
24
|
+
(load-file (expand-file-name "ox-fronde.el" "{{ fronde_data_dir }}"))
|
@@ -60,7 +60,7 @@ INFO is a plist used as a communication channel."
|
|
60
60
|
output)
|
61
61
|
(push `(?l . ,(org-export-data (plist-get info :language) info)) output)
|
62
62
|
(push `(?n . ,(format "Fronde %s" fronde/version)) output)
|
63
|
-
(push `(?N . ,(format "<a href=\"https://
|
63
|
+
(push `(?N . ,(format "<a href=\"https://etienne.depar.is/fronde/\">Fronde</a> %s" fronde/version)) output)
|
64
64
|
(push `(?x . ,(org-export-data (plist-get info :description) info)) output)
|
65
65
|
(push `(?X . ,(format "<p>%s</p>"
|
66
66
|
(org-export-data (plist-get info :description) info)))
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'liquid'
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
module Fronde
|
7
|
+
module Config
|
8
|
+
# Various utilitaries methods
|
9
|
+
module Helpers
|
10
|
+
def self.extract_lang_from_env(default)
|
11
|
+
(ENV['LANG'] || default).split('_', 2).first
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.migrate(config)
|
15
|
+
return config unless config.has_key?('public_folder')
|
16
|
+
|
17
|
+
warn R18n.t.fronde.error.config.deprecated_public_folder
|
18
|
+
old_pub_folder = config.delete('public_folder')
|
19
|
+
return config if config.has_key?('html_public_folder')
|
20
|
+
|
21
|
+
config['html_public_folder'] = old_pub_folder
|
22
|
+
config
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.ensure_expanded_paths(config)
|
26
|
+
%w[html gemini].each do |what|
|
27
|
+
key = "#{what}_public_folder"
|
28
|
+
config[key] = File.expand_path config[key]
|
29
|
+
end
|
30
|
+
config
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate emacs directory variables file.
|
34
|
+
#
|
35
|
+
# This method generate the file ~.dir-locals.el~, which is
|
36
|
+
# responsible to load fronde Org settings when visiting an Org file
|
37
|
+
# of this fronde instance.
|
38
|
+
#
|
39
|
+
# @return [Integer] the length written (as returned by the
|
40
|
+
# underlying ~File.write~ method call)
|
41
|
+
def self.write_dir_locals
|
42
|
+
workdir = Dir.pwd
|
43
|
+
# rubocop:disable Layout/LineLength
|
44
|
+
File.write(
|
45
|
+
"#{workdir}/.dir-locals.el",
|
46
|
+
"((org-mode . ((eval . (load-file \"#{workdir}/var/lib/org-config.el\")))))"
|
47
|
+
)
|
48
|
+
# rubocop:enable Layout/LineLength
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.render_liquid_template(content, data)
|
52
|
+
template = Liquid::Template.parse(content)
|
53
|
+
template.render(data)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Filter for liquid templates
|
58
|
+
module Filters
|
59
|
+
def cast_lisp_value(value, key)
|
60
|
+
return 't' if value.is_a?(TrueClass)
|
61
|
+
return 'nil' if value.nil? || value.is_a?(FalseClass)
|
62
|
+
|
63
|
+
value = value.strip
|
64
|
+
lisp_keywords = ['t', 'nil', '1', '-1', '0']
|
65
|
+
if key.end_with?('-function') || lisp_keywords.include?(value)
|
66
|
+
return value
|
67
|
+
end
|
68
|
+
|
69
|
+
value.gsub!('"', '\"')
|
70
|
+
%("#{value}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def md5(value)
|
74
|
+
Digest::MD5.hexdigest(value)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Liquid::Template.register_filter(Fronde::Config::Filters)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'open-uri'
|
5
|
+
require_relative '../version'
|
6
|
+
require_relative '../org'
|
7
|
+
require_relative 'helpers'
|
8
|
+
|
9
|
+
require_relative '../../ext/r18n'
|
10
|
+
using R18nPatch
|
11
|
+
|
12
|
+
module Fronde
|
13
|
+
module Config
|
14
|
+
# This module contains utilitary methods to ease ~org-config.el~
|
15
|
+
# file generation
|
16
|
+
module Lisp
|
17
|
+
# Generate emacs lisp configuration file for Org and write it.
|
18
|
+
#
|
19
|
+
# This method saves the generated configuration in the file
|
20
|
+
# ~org-config.el~ at the root of your project, overwriting it if it
|
21
|
+
# existed already.
|
22
|
+
#
|
23
|
+
# @return [Integer] the length written (as returned by the
|
24
|
+
# underlying ~File.write~ method call)
|
25
|
+
# rubocop:disable Metrics/MethodLength
|
26
|
+
def write_org_lisp_config
|
27
|
+
workdir = Dir.pwd
|
28
|
+
all_projects = sources.map(&:org_config).flatten
|
29
|
+
all_themes = org_generate_themes(all_projects)
|
30
|
+
FileUtils.mkdir_p "#{workdir}/var/lib"
|
31
|
+
content = Helpers.render_liquid_template(
|
32
|
+
File.read(File.expand_path('./data/org-config.el', __dir__)),
|
33
|
+
'version' => Fronde::VERSION,
|
34
|
+
'work_dir' => workdir,
|
35
|
+
'fronde_data_dir' => File.expand_path('data', __dir__),
|
36
|
+
'org_version' => Fronde::Org.current_version,
|
37
|
+
'long_date_fmt' => R18n.t.full_datetime_format.to_s,
|
38
|
+
'author' => { 'email' => get('author_email', ''),
|
39
|
+
'name' => get('author') },
|
40
|
+
'all_projects' => all_projects + all_themes
|
41
|
+
)
|
42
|
+
File.write("#{workdir}/var/lib/org-config.el", content)
|
43
|
+
end
|
44
|
+
# rubocop:enable Metrics/MethodLength
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def org_theme_config(theme)
|
49
|
+
{ 'base-directory' => File.expand_path("themes/#{theme}"),
|
50
|
+
# rubocop:disable Layout/LineLength
|
51
|
+
'base-extension' => %w[css js gif jpg png svg otf ttf woff2?].join('\\\\|'),
|
52
|
+
'publishing-directory' => "#{get('html_public_folder')}/assets/#{theme}",
|
53
|
+
# rubocop:enable Layout/LineLength
|
54
|
+
'publishing-function' => 'org-publish-attachment',
|
55
|
+
'recursive' => true }
|
56
|
+
end
|
57
|
+
|
58
|
+
def org_generate_themes(projects)
|
59
|
+
all_themes = projects.filter_map { |project| project['theme'] }
|
60
|
+
all_themes << get('theme')
|
61
|
+
all_themes.uniq.compact.filter_map do |theme|
|
62
|
+
next if theme == 'default'
|
63
|
+
|
64
|
+
{ 'name' => "theme-#{theme}",
|
65
|
+
'attributes' => org_theme_config(theme) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/fronde/config.rb
CHANGED
@@ -1,44 +1,62 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
|
-
require '
|
4
|
+
require 'r18n-core'
|
5
|
+
require 'singleton'
|
6
|
+
|
7
|
+
require_relative 'config/lisp'
|
8
|
+
require_relative 'source'
|
5
9
|
|
6
10
|
module Fronde
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
11
|
+
module Config
|
12
|
+
# Wrapper for configuration
|
13
|
+
#
|
14
|
+
# This class is a singleton interface, which share the static website
|
15
|
+
# being build settings among different steps or tasks.
|
16
|
+
#
|
17
|
+
# It expects the website author to holds their custom settings in a
|
18
|
+
# YAML file named ~config.yml~ available at the root of their
|
19
|
+
# project.
|
20
|
+
#
|
21
|
+
# For example, with the given config file:
|
22
|
+
#
|
23
|
+
# #+begin_src
|
24
|
+
# ---
|
25
|
+
# title: My website
|
26
|
+
# author: Alice Doe
|
27
|
+
# #+end_src
|
28
|
+
#
|
29
|
+
# Settings will be available like this:
|
30
|
+
#
|
31
|
+
# #+begin_src
|
32
|
+
# Fronde::CONFIG.get('author')
|
33
|
+
# => "Alice Doe"
|
34
|
+
# #+end_src
|
35
|
+
class Store
|
36
|
+
include Singleton
|
37
|
+
|
38
|
+
attr_reader :sources
|
39
|
+
|
40
|
+
def initialize
|
41
|
+
@default_settings = {
|
42
|
+
'author' => (ENV['USER'] || ''),
|
43
|
+
'domain' => '',
|
44
|
+
'lang' => Fronde::Config::Helpers.extract_lang_from_env('en'),
|
45
|
+
'html_public_folder' => 'public_html',
|
46
|
+
'gemini_public_folder' => 'public_gmi',
|
47
|
+
'templates' => [], 'theme' => 'default'
|
48
|
+
}.freeze
|
49
|
+
@org_version = @sources = nil
|
50
|
+
@config = load_settings
|
51
|
+
# Do not load sources now to avoid dependency loop on config
|
52
|
+
end
|
53
|
+
|
54
|
+
include Fronde::Config::Lisp
|
55
|
+
|
34
56
|
# Access the current website settings
|
35
57
|
#
|
36
|
-
# If necessary, this method will load settings from a config
|
37
|
-
# file.
|
38
|
-
#
|
39
58
|
# @return [Hash] the website settings
|
40
59
|
def settings
|
41
|
-
load_settings
|
42
60
|
@config
|
43
61
|
end
|
44
62
|
|
@@ -56,7 +74,6 @@ module Fronde
|
|
56
74
|
# @param default the default value to use if ~setting~ is absent
|
57
75
|
# @return the setting value or nil
|
58
76
|
def get(setting, default = nil)
|
59
|
-
load_settings
|
60
77
|
if setting.is_a? Array
|
61
78
|
value = @config.dig(*setting)
|
62
79
|
else
|
@@ -65,31 +82,6 @@ module Fronde
|
|
65
82
|
value || default
|
66
83
|
end
|
67
84
|
|
68
|
-
# Save the settings given as a parameter to the ~config.yml~ file.
|
69
|
-
#
|
70
|
-
# Not only this method overwrite the old settings, but it replace
|
71
|
-
# the current shared settings with the ones given in
|
72
|
-
# parameter. Later call to
|
73
|
-
# {file:Fronde/Config.html#settings-class_method settings}
|
74
|
-
# will, obviously, use these new settings.
|
75
|
-
#
|
76
|
-
# @param new_config [Hash] the settings to save
|
77
|
-
# @return [Hash] the new settings after save
|
78
|
-
def save(new_config)
|
79
|
-
# Do not save obvious default config values. We'll always try to
|
80
|
-
# save author and lang as they default on system variables,
|
81
|
-
# which may be different from a system to another. Thus it may
|
82
|
-
# be confusing if one use fronde on two different computer and
|
83
|
-
# these params always change.
|
84
|
-
default_keys = default_settings.keys
|
85
|
-
new_config.delete_if do |k, v|
|
86
|
-
default_keys.include?(k) && v == default_settings[k]
|
87
|
-
end
|
88
|
-
File.write 'config.yml', new_config.to_yaml
|
89
|
-
@config = @sources = nil
|
90
|
-
load_settings # Reload config, taking default settings into account
|
91
|
-
end
|
92
|
-
|
93
85
|
# Reset settings
|
94
86
|
#
|
95
87
|
# This method is handy for testing purpose. Next call to
|
@@ -99,7 +91,10 @@ module Fronde
|
|
99
91
|
#
|
100
92
|
# @return nil
|
101
93
|
def reset
|
102
|
-
|
94
|
+
# Reload config, taking default settings into account
|
95
|
+
@config = load_settings
|
96
|
+
@org_version = @sources = nil
|
97
|
+
@sources = load_sources
|
103
98
|
end
|
104
99
|
|
105
100
|
# Load the given settings as if they comes from the ~config.yml~ file.
|
@@ -111,68 +106,109 @@ module Fronde
|
|
111
106
|
# use these new settings.
|
112
107
|
#
|
113
108
|
# @param config [Hash] the settings to artificially load
|
114
|
-
# @return [
|
109
|
+
# @return [Fronde::Config::Store] self
|
115
110
|
def load_test(config)
|
116
|
-
|
117
|
-
@
|
118
|
-
sources
|
119
|
-
|
111
|
+
@config = @default_settings.merge config
|
112
|
+
@org_version = @sources = nil
|
113
|
+
@sources = load_sources
|
114
|
+
self
|
120
115
|
end
|
121
116
|
|
122
117
|
# Return the qualified projects sources list.
|
123
118
|
#
|
124
119
|
# @return [Array] the fully qualified projects sources list
|
125
|
-
def
|
120
|
+
def load_sources
|
126
121
|
return @sources if @sources
|
127
|
-
|
128
|
-
|
129
|
-
@sources = get('sources', default_sources).map do |s|
|
130
|
-
build_source(s)
|
131
|
-
end.compact
|
122
|
+
|
123
|
+
@sources = remove_inclusion(remove_duplicate(build_sources))
|
132
124
|
end
|
133
125
|
|
134
126
|
private
|
135
127
|
|
136
128
|
def load_settings
|
137
|
-
return @config if @config
|
138
129
|
conf_file = 'config.yml'
|
130
|
+
user_conf = {}
|
139
131
|
if File.exist? conf_file
|
140
|
-
|
141
|
-
|
142
|
-
@config = default_settings
|
132
|
+
user_conf = YAML.load_file(conf_file)
|
133
|
+
user_conf = Fronde::Config::Helpers.migrate(user_conf)
|
143
134
|
end
|
135
|
+
user_conf = @default_settings.merge(user_conf)
|
136
|
+
Fronde::Config::Helpers.ensure_expanded_paths(user_conf).freeze
|
144
137
|
end
|
145
138
|
|
146
|
-
def
|
147
|
-
|
139
|
+
def build_sources
|
140
|
+
default_sources = [{ 'path' => 'src', 'target' => '.' }]
|
141
|
+
get('sources', default_sources).filter_map do |source_conf|
|
142
|
+
config = Source.canonical_config source_conf.dup
|
143
|
+
unless config['path']
|
144
|
+
warn R18n.t.fronde.error.source.no_path(source: config.inspect)
|
145
|
+
next
|
146
|
+
end
|
147
|
+
Source.new_from_config config
|
148
|
+
end
|
148
149
|
end
|
149
150
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
151
|
+
def remove_duplicate(sources)
|
152
|
+
check_paths = {}
|
153
|
+
sources.each do |source|
|
154
|
+
type = source.type
|
155
|
+
check_paths[type] ||= {}
|
156
|
+
path = source['path']
|
157
|
+
# Avoid duplicate
|
158
|
+
if check_paths[type].has_key?(path)
|
159
|
+
warn(
|
160
|
+
R18n.t.fronde.error.source.duplicate(
|
161
|
+
source: source['name'], type: type
|
162
|
+
)
|
163
|
+
)
|
164
|
+
next
|
165
|
+
end
|
166
|
+
check_paths[type][path] = source
|
167
|
+
end
|
168
|
+
check_paths
|
160
169
|
end
|
161
170
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
171
|
+
def remove_inclusion(check_paths)
|
172
|
+
check_paths.map do |type, sources_by_path|
|
173
|
+
skip_paths = []
|
174
|
+
|
175
|
+
# Check paths in the right order
|
176
|
+
sorted_paths = sources_by_path.keys.sort_by(&:length)
|
177
|
+
sorted_paths.filter_map do |path|
|
178
|
+
next if skip_paths.include?(path)
|
179
|
+
|
180
|
+
source = sources_by_path[path]
|
181
|
+
# If current source is not recursive, there is no possible
|
182
|
+
# issue
|
183
|
+
next source unless source.recursive?
|
184
|
+
|
185
|
+
# Ensure that the current source does not embed another one
|
186
|
+
possible_matchs = sorted_paths.select do |other_path|
|
187
|
+
path != other_path && other_path.start_with?(path)
|
188
|
+
end
|
189
|
+
next source if possible_matchs.empty?
|
190
|
+
|
191
|
+
skip_paths += possible_matchs
|
192
|
+
possible_matchs.each do |match|
|
193
|
+
other_source = sources_by_path[match]
|
194
|
+
warn(
|
195
|
+
R18n.t.fronde.error.source.inclusion(
|
196
|
+
source: other_source['title'], type: type,
|
197
|
+
other_source: source['title']
|
198
|
+
)
|
199
|
+
)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end.flatten
|
175
203
|
end
|
176
204
|
end
|
177
205
|
end
|
206
|
+
|
207
|
+
CONFIG = Config::Store.instance
|
178
208
|
end
|
209
|
+
|
210
|
+
R18n.default_places = File.expand_path('../../locales', __dir__)
|
211
|
+
R18n::Filters.on(:named_variables)
|
212
|
+
R18n.set Fronde::CONFIG.get('lang')
|
213
|
+
|
214
|
+
Fronde::CONFIG.load_sources
|