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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/bin/fronde +15 -30
  3. data/lib/ext/nil_time.rb +25 -0
  4. data/lib/ext/r18n.rb +17 -0
  5. data/lib/ext/time.rb +49 -0
  6. data/lib/fronde/cli/commands.rb +92 -103
  7. data/lib/fronde/cli/data/Rakefile +8 -0
  8. data/lib/fronde/cli/data/config.yml +13 -0
  9. data/lib/fronde/cli/data/gitignore +7 -0
  10. data/lib/fronde/cli/data/zsh_completion +37 -0
  11. data/lib/fronde/cli/helpers.rb +55 -0
  12. data/lib/fronde/cli/opt_parse.rb +143 -0
  13. data/lib/fronde/cli/throbber.rb +99 -0
  14. data/lib/fronde/cli.rb +41 -42
  15. data/lib/fronde/config/data/org-config.el +24 -0
  16. data/lib/fronde/config/{ox-fronde.el → data/ox-fronde.el} +1 -1
  17. data/lib/fronde/config/helpers.rb +80 -0
  18. data/lib/fronde/config/lisp.rb +70 -0
  19. data/lib/fronde/config.rb +135 -99
  20. data/lib/fronde/emacs.rb +23 -20
  21. data/lib/fronde/index/atom_generator.rb +55 -66
  22. data/lib/fronde/index/data/all_tags.org +14 -0
  23. data/lib/fronde/index/data/template.org +22 -0
  24. data/lib/fronde/index/data/template.xml +37 -0
  25. data/lib/fronde/index/org_generator.rb +70 -88
  26. data/lib/fronde/index.rb +56 -82
  27. data/lib/fronde/org/file.rb +287 -0
  28. data/lib/fronde/org/file_extracter.rb +98 -0
  29. data/lib/fronde/org.rb +103 -0
  30. data/lib/fronde/preview.rb +43 -39
  31. data/lib/fronde/slug.rb +27 -0
  32. data/lib/fronde/source/gemini.rb +39 -0
  33. data/lib/fronde/source/html.rb +67 -0
  34. data/lib/fronde/source.rb +204 -0
  35. data/lib/fronde/templater.rb +94 -71
  36. data/lib/fronde/version.rb +1 -1
  37. data/lib/tasks/cli.rake +33 -0
  38. data/lib/tasks/org.rake +62 -42
  39. data/lib/tasks/site.rake +68 -30
  40. data/lib/tasks/sync.rake +41 -21
  41. data/lib/tasks/tags.rake +11 -7
  42. data/locales/en.yml +60 -14
  43. data/locales/fr.yml +68 -14
  44. metadata +53 -110
  45. data/lib/fronde/config/lisp_config.rb +0 -340
  46. data/lib/fronde/config/org-config.el +0 -19
  47. data/lib/fronde/org_file/class_methods.rb +0 -72
  48. data/lib/fronde/org_file/extracter.rb +0 -72
  49. data/lib/fronde/org_file/htmlizer.rb +0 -43
  50. data/lib/fronde/org_file.rb +0 -298
  51. 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
- require 'fronde/cli/commands'
4
+ require_relative 'cli/commands'
5
5
 
6
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
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
- include Fronde::CLICommands
16
+ include Commands
16
17
 
17
- private
18
+ def run(argv)
19
+ @argv = argv
20
+ @command = OptParse.resolve_possible_alias(@argv.shift || 'basic')
18
21
 
19
- def init_required_files
20
- init_rakefile unless File.exist?('Rakefile')
21
- init_gitignore unless File.exist?('.gitignore')
22
- end
22
+ if help_param_given?
23
+ return 2 if @options[:recover_from_error]
24
+ return 0
25
+ end
23
26
 
24
- def init_rake
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
- def init_rakefile
31
- rakefile = <<~RAKE
32
- # frozen_string_literal: true
29
+ method = "fronde_#{@command}".to_sym
30
+ return 2 if method_unknown?(method)
33
31
 
34
- require 'fronde/config'
35
- require 'r18n-core'
32
+ send method
33
+ end
36
34
 
37
- fronde_spec = Gem::Specification.find_by_name 'fronde'
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
- Dir.glob("\#{fronde_spec.gem_dir}/lib/tasks/*.rake").each { |r| import r }
37
+ def init_rake
38
+ @rake = Rake.application
39
+ Rake.verbose(false) unless @options[:verbose]
40
+ @rake.load_rakefile
41
+ end
43
42
 
44
- task default: 'site:build'
45
- RAKE
46
- File.write 'Rakefile', rakefile
47
- end
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
- def init_gitignore
50
- gitignore = <<~GITIGNORE
51
- .dir-locals.el
52
- Rakefile
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://git.umaneti.net/fronde/about/\">Fronde</a> %s" fronde/version)) output)
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 'fronde/config/lisp_config'
4
+ require 'r18n-core'
5
+ require 'singleton'
6
+
7
+ require_relative 'config/lisp'
8
+ require_relative 'source'
5
9
 
6
10
  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.get('author')
28
- # => "Alice Doe"
29
- # #+end_src
30
- class Config
31
- extend Fronde::LispConfig
32
-
33
- class << self
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
- @sources = @config = nil
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 [Hash] the new settings
109
+ # @return [Fronde::Config::Store] self
115
110
  def load_test(config)
116
- reset
117
- @config = default_settings.merge config
118
- sources
119
- @config
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 sources
120
+ def load_sources
126
121
  return @sources if @sources
127
- load_settings
128
- default_sources = [{ 'path' => 'src', 'target' => '.' }]
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
- @config = default_settings.merge(YAML.load_file(conf_file)).freeze
141
- else
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 extract_lang_from_env(default)
147
- (ENV['LANG'] || default).split('_', 2).first
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 default_settings
151
- return @default_settings if @default_settings
152
- @default_settings = {
153
- 'author' => (ENV['USER'] || ''),
154
- 'domain' => '',
155
- 'lang' => extract_lang_from_env('en'),
156
- 'public_folder' => 'public_html',
157
- 'templates' => [],
158
- 'theme' => 'default'
159
- }.freeze
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 build_source(seed)
163
- opts = { 'recursive' => true, 'is_blog' => false }
164
- case seed
165
- when String
166
- opts['path'] = seed
167
- when Hash
168
- opts.merge! seed
169
- end
170
- return nil unless opts.has_key?('path')
171
- opts['path'] = File.expand_path(opts['path'])
172
- opts['name'] ||= File.basename(opts['path']).sub(/^\./, '')
173
- opts['target'] ||= opts['name']
174
- opts
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