dimples 4.3.2 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6bdfb2eae683d3ef78e118e1096319d376db8434
4
- data.tar.gz: 402257f0a0610c45baef474482acbe5dc8bf7d94
2
+ SHA256:
3
+ metadata.gz: d7a2db6d510cc724696cd9255569144515c7ad9afd10b4a6da596f50f492d4f9
4
+ data.tar.gz: f55f43d79d5137c22ccea11e27c50be6de043ee794f3d3ae3ea212508da6bebb
5
5
  SHA512:
6
- metadata.gz: 81064093be0ee7d0c8dc535d99a49e2be396fc5d00132b83c6b869161d5fed9afddd7fdda1616feb496f1d86e235865230ab0fe7de07525e123a188b75954882
7
- data.tar.gz: fb65a564e482f7210beb4fa62c1e881847bbe1e0005ddaa1ce97661f4aae72a70a5ef77e0508f7b1e4ba4167c38aafdf999e9e0e03a7337effc565c476347333
6
+ metadata.gz: c86532b5690b9bc411d18357101799fe3e6e9c0a2c33ef9fabc2f3975b3fa9551cb45ae97768dcd96a6b0ec7388ceaba4bcdf0dc5a40b3a800ce10b3878c3085
7
+ data.tar.gz: 815be09d293fba723038a6fb0bbc6123434e03b34bdd7aa52438f276b234ea9e52cf661c2efa0c1120d72533c2149a81b2e5f8a87381ba1f666108b9166ed566
data/bin/dimples CHANGED
@@ -18,7 +18,7 @@ valid_commands = %w[build]
18
18
  options = Trollop.options do
19
19
  version "dimples v#{Dimples::VERSION}"
20
20
  banner <<-BANNER
21
- A very, very simple static site generator.
21
+ A simple static site generator.
22
22
 
23
23
  Usage:
24
24
  dimples <#{valid_commands.join('|')}> [options]
@@ -26,7 +26,6 @@ dimples <#{valid_commands.join('|')}> [options]
26
26
  Options:
27
27
  BANNER
28
28
  opt :config, 'Config file path', type: :string
29
- opt :lib, 'Library file path', default: 'lib'
30
29
  opt :verbose, 'Verbose mode', default: false
31
30
  end
32
31
 
@@ -37,7 +36,7 @@ unless valid_commands.include?(command)
37
36
  Trollop.die "Command must be '#{valid_commands.join('\', \'')}'"
38
37
  end
39
38
 
40
- lib_path = File.join(Dir.pwd, options[:lib])
39
+ plugins_path = File.join(Dir.pwd, 'plugins')
41
40
  config_path = options[:config] || File.join(Dir.pwd, 'config.json')
42
41
 
43
42
  if File.exist?(config_path)
@@ -53,43 +52,24 @@ else
53
52
  Trollop.die "Unable to find config file (#{config_path})"
54
53
  end
55
54
 
56
- config[:verbose_logging] = options[:verbose] if options[:verbose]
57
-
58
- if Dir.exist?(lib_path)
59
- Dir.glob(File.join(lib_path, '**', '*.rb')) do |path|
55
+ if Dir.exist?(plugins_path)
56
+ Dir.glob(File.join(plugins_path, '**', '*.rb')) do |path|
60
57
  require path
61
58
  end
62
59
  end
63
60
 
64
- site_klass_name = config.dig(:class_overrides, :site)
65
-
66
- site_klass = if site_klass_name
67
- Object.const_get(site_klass_name)
68
- else
69
- Dimples::Site
70
- end
71
-
72
- site = site_klass.new(config)
61
+ site = Dimples::Site.new(config)
73
62
 
74
63
  case command.to_sym
75
64
  when :build
76
- Dimples.logger.info("Building site at #{site.output_paths[:site]}...")
77
-
78
- result = Benchmark.measure do
79
- site.generate
80
- end
81
-
82
- if site.generated?
83
- generation_time = result.real.round(2)
65
+ puts 'Building site...'
84
66
 
85
- message = "\033[92mDone!\033[0m Site built in #{generation_time} second"
86
- message += 's' if generation_time != 1
87
- message += '.'
67
+ site.generate
88
68
 
89
- Dimples.logger.info(message)
69
+ if site.errors.empty?
70
+ puts 'Done! Your site has been built.'
90
71
  else
91
- site.errors.each do |error|
92
- Dimples.logger.error(error)
93
- end
72
+ puts 'Generation failed:'
73
+ site.errors.each { |error| puts error }
94
74
  end
95
75
  end
data/lib/dimples.rb CHANGED
@@ -2,30 +2,19 @@
2
2
 
3
3
  $LOAD_PATH.unshift(__dir__)
4
4
 
5
- require 'benchmark'
6
- require 'fileutils'
7
- require 'logger'
5
+ require 'hashie'
8
6
  require 'tilt'
9
7
  require 'yaml'
10
8
 
11
9
  require 'dimples/frontable'
12
- require 'dimples/renderable'
13
10
 
14
11
  require 'dimples/category'
15
12
  require 'dimples/configuration'
16
13
  require 'dimples/errors'
17
- require 'dimples/logger'
18
14
  require 'dimples/page'
19
- require 'dimples/pagination'
15
+ require 'dimples/pager'
16
+ require 'dimples/plugin'
20
17
  require 'dimples/post'
18
+ require 'dimples/renderer'
21
19
  require 'dimples/site'
22
20
  require 'dimples/template'
23
-
24
- # A static site generator.
25
- module Dimples
26
- class << self
27
- def logger
28
- @logger ||= Dimples::Logger.new(STDOUT)
29
- end
30
- end
31
- end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dimples
4
- # A class that models a single interview category.
5
4
  class Category
6
5
  attr_accessor :name
7
6
  attr_accessor :slug
@@ -10,12 +9,13 @@ module Dimples
10
9
  def initialize(site, slug)
11
10
  @site = site
12
11
  @slug = slug
13
- @name = @site.config[:category_names][slug.to_sym] || slug.capitalize
12
+ @name = @site.config.category_names[slug.to_sym] || slug.capitalize
13
+
14
14
  @posts = []
15
15
  end
16
16
 
17
17
  def inspect
18
- "#<#{self.class} @slug=#{@slug} @name=#{@name}>"
18
+ "#<#{self.class} @slug=#{slug} @name=#{name}>"
19
19
  end
20
20
  end
21
21
  end
@@ -1,74 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dimples
4
- # A class that models a site's configuration.
5
- class Configuration
6
- def initialize(config = {})
7
- @settings = Dimples::Configuration.default_settings
8
-
9
- config.each_key do |key|
10
- case @settings[key]
11
- when Hash
12
- @settings[key].merge!(config[key])
13
- else
14
- @settings[key] = config[key]
15
- end
16
- end
17
- end
18
-
19
- def [](key)
20
- @settings[key]
21
- end
22
-
23
- def self.default_settings
4
+ module Configuration
5
+ def self.defaults
24
6
  {
25
- source_path: Dir.pwd,
26
- destination_path: File.join(Dir.pwd, 'site'),
27
- verbose_logging: false,
28
- class_overrides: { site: nil, post: nil },
29
- rendering: {},
30
- category_names: {},
31
7
  paths: default_paths,
8
+ generation: default_generation,
32
9
  layouts: default_layouts,
33
10
  pagination: default_pagination,
34
- generation: default_generation,
35
- date_formats: default_date_formats
36
- }
37
- end
38
-
39
- def self.default_layouts
40
- {
41
- posts: 'posts',
42
- post: 'post',
43
- category: 'category',
44
- year_archives: 'year_archives',
45
- month_archives: 'month_archives',
46
- day_archives: 'day_archives'
11
+ date_formats: default_date_formats,
12
+ feed_formats: default_feed_formats,
13
+ category_names: {},
14
+ rendering: {},
47
15
  }
48
16
  end
49
17
 
50
18
  def self.default_paths
51
19
  {
20
+ output: 'site',
52
21
  archives: 'archives',
53
22
  posts: 'archives/%Y/%m/%d',
54
23
  categories: 'archives/categories'
55
24
  }
56
25
  end
57
26
 
58
- def self.default_pagination
59
- {
60
- per_page: 10
61
- }
62
- end
63
-
64
27
  def self.default_generation
65
28
  {
66
- categories: true,
29
+ archives: true,
67
30
  year_archives: true,
68
31
  month_archives: true,
69
32
  day_archives: true,
70
- feeds: true,
71
- category_feeds: true
33
+ categories: true,
34
+ main_feed: true,
35
+ category_feeds: true,
36
+ }
37
+ end
38
+
39
+ def self.default_layouts
40
+ {
41
+ post: 'post',
42
+ category: 'category',
43
+ archive: 'archive',
44
+ date_archive: 'archive'
72
45
  }
73
46
  end
74
47
 
@@ -79,5 +52,16 @@ module Dimples
79
52
  day: '%Y-%m-%d'
80
53
  }
81
54
  end
55
+
56
+ def self.default_feed_formats
57
+ ['atom']
58
+ end
59
+
60
+ def self.default_pagination
61
+ {
62
+ page_prefix: 'page',
63
+ per_page: 10
64
+ }
65
+ end
82
66
  end
83
67
  end
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dimples
4
- module Errors
5
- class PublishingError < StandardError
6
- end
4
+ class PublishingError < StandardError
5
+ end
7
6
 
8
- class RenderingError < StandardError
9
- end
7
+ class RenderingError < StandardError
8
+ end
10
9
 
11
- class GenerationError < StandardError
12
- end
10
+ class GenerationError < StandardError
13
11
  end
14
12
  end
@@ -1,23 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dimples
4
- # A mixin class that handles reading and parsing front matter from a file.
5
4
  module Frontable
6
- SKIPPED_METADATA_KEYS = %w[site path contents].freeze
5
+ def read_with_front_matter(path)
6
+ contents = File.read(path)
7
7
 
8
- def read_with_front_matter
9
- @contents = File.read(@path)
10
-
11
- matches = @contents.match(/^(-{3}\n.*?\n?)^(-{3}*$\n?)/m)
12
- return if matches.nil?
13
-
14
- YAML.safe_load(matches[1]).each_pair do |key, value|
15
- if !SKIPPED_METADATA_KEYS.include?(key) && respond_to?("#{key}=")
16
- send("#{key}=", value)
17
- end
8
+ if (matches = contents.match(/^(-{3}\n.*?\n?)^(-{3}*$\n?)/m))
9
+ metadata = Hashie.symbolize_keys(YAML.safe_load(matches[1]))
10
+ contents = matches.post_match.strip
11
+ else
12
+ metadata = {}
18
13
  end
19
14
 
20
- @contents = matches.post_match.strip
15
+ [contents, metadata]
21
16
  end
22
17
  end
23
18
  end
data/lib/dimples/page.rb CHANGED
@@ -1,67 +1,73 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dimples
4
- # A class that models a single site page.
5
4
  class Page
6
5
  include Frontable
7
- include Renderable
8
6
 
9
- attr_accessor :path
10
- attr_accessor :title
11
- attr_accessor :filename
12
- attr_accessor :extension
13
- attr_accessor :layout
14
7
  attr_accessor :contents
15
- attr_accessor :output_directory
8
+ attr_accessor :metadata
9
+ attr_accessor :path
16
10
 
17
11
  def initialize(site, path = nil)
18
12
  @site = site
19
- @extension = 'html'
20
13
  @path = path
21
14
 
22
15
  if @path
23
- @filename = File.basename(@path, File.extname(@path))
24
- @output_directory = File.dirname(@path).sub(
25
- @site.source_paths[:pages],
26
- @site.output_paths[:site]
27
- )
28
-
29
- read_with_front_matter
16
+ @contents, @metadata = read_with_front_matter(@path)
30
17
  else
31
- @filename = 'index'
32
18
  @contents = ''
33
- @output_directory = @site.output_paths[:site]
19
+ @metadata = {}
34
20
  end
35
21
  end
36
22
 
37
- def output_path
38
- File.join(@output_directory, output_filename)
23
+ def filename
24
+ @metadata[:filename] || 'index'
39
25
  end
40
26
 
41
- def output_filename
42
- "#{@filename}.#{@extension}"
27
+ def extension
28
+ @metadata[:extension] || 'html'
43
29
  end
44
30
 
45
- def url
46
- absolute_output_directory = File.absolute_path(@output_directory)
31
+ def render(context = {})
32
+ metadata = @metadata.dup
33
+ metadata.merge!(context[:page]) if context[:page]
47
34
 
48
- absolute_output_directory.sub(@site.output_paths[:site], '').tap do |url|
49
- url[0] = '/' unless url[0] == '/'
50
- url.concat('/') unless url[-1] == '/'
51
- url.concat(output_filename) if filename != 'index'
52
- end
35
+ context[:page] = Hashie::Mash.new(metadata)
36
+
37
+ renderer.render(context)
53
38
  end
54
39
 
55
- def write(context = {})
56
- FileUtils.mkdir_p(@output_directory) unless Dir.exist?(@output_directory)
40
+ def write(output_directory, context = {})
41
+ FileUtils.mkdir_p(output_directory) unless Dir.exist?(output_directory)
42
+ output_path = File.join(output_directory, "#{filename}.#{extension}")
43
+
57
44
  File.write(output_path, render(context))
58
45
  rescue SystemCallError => e
59
- error_message = "Failed to write #{path} (#{e.message})"
60
- raise Errors::PublishingError, error_message
46
+ raise PublishingError, "Failed to publish file at #{output_path} (#{e})"
61
47
  end
62
48
 
63
49
  def inspect
64
- "#<#{self.class} @output_path=#{output_path}>"
50
+ "#<#{self.class} @path=#{path}>"
51
+ end
52
+
53
+ private
54
+
55
+ def renderer
56
+ @renderer ||= Renderer.new(@site, self)
57
+ end
58
+
59
+ def method_missing(method_name, *args, &block)
60
+ if @metadata.key?(method_name)
61
+ @metadata[method_name]
62
+ elsif (matches = method_name.match(/([a-z_]+)=/))
63
+ @metadata[matches[1].to_sym] = args[0]
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ def respond_to_missing?(method_name, include_private = false)
70
+ @metadata.key?(method_name) || method_name.match?(/([a-z_]+)=/) || super
65
71
  end
66
72
  end
67
73
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dimples
4
+ class Pager
5
+ include Enumerable
6
+
7
+ attr_reader :current_page
8
+ attr_reader :previous_page
9
+ attr_reader :next_page
10
+ attr_reader :page_count
11
+ attr_reader :item_count
12
+
13
+ def initialize(url, posts, options = {})
14
+ @url = url
15
+ @posts = posts
16
+ @per_page = options[:per_page] || 10
17
+ @page_prefix = options[:page_prefix] || 'page'
18
+ @page_count = (posts.length.to_f / @per_page.to_i).ceil
19
+
20
+ step_to(1)
21
+ end
22
+
23
+ def each(&block)
24
+ (1..@page_count).each do |index|
25
+ block.yield step_to(index)
26
+ end
27
+ end
28
+
29
+ def step_to(page)
30
+ @current_page = (1..@page_count).cover?(page) ? page : 1
31
+ @previous_page = (@current_page - 1).positive? ? @current_page - 1 : nil
32
+ @next_page = @current_page + 1 <= @page_count ? @current_page + 1 : nil
33
+
34
+ @current_page
35
+ end
36
+
37
+ def posts_at(page)
38
+ @posts.slice((page - 1) * @per_page, @per_page)
39
+ end
40
+
41
+ def current_page_url
42
+ @current_page != 1 ? "#{@url}#{@page_prefix}#{@current_page}" : @url
43
+ end
44
+
45
+ def first_page_url
46
+ @url
47
+ end
48
+
49
+ def last_page_url
50
+ @page_count != 1 ? "#{@url}#{@page_prefix}#{@page_count}" : @url
51
+ end
52
+
53
+ def previous_page_url
54
+ return unless @previous_page
55
+ @previous_page != 1 ? "#{@url}#{@page_prefix}#{@previous_page}" : @url
56
+ end
57
+
58
+ def next_page_url
59
+ "#{@url}#{@page_prefix}#{@next_page}" if @next_page
60
+ end
61
+
62
+ def to_context
63
+ Hashie::Mash.new(
64
+ posts: posts_at(current_page),
65
+ current_page: @current_page,
66
+ page_count: @page_count,
67
+ post_count: @posts.count,
68
+ previous_page: @previous_page,
69
+ next_page: @next_page,
70
+ urls: {
71
+ current_page: current_page_url,
72
+ first_page: first_page_url,
73
+ last_page: last_page_url,
74
+ previous_page: previous_page_url,
75
+ next_page: next_page_url
76
+ }
77
+ )
78
+ end
79
+ end
80
+ end