mill 0.11 → 0.18

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -1
  3. data/LICENSE +1 -1
  4. data/Rakefile +1 -2
  5. data/TODO.md +4 -0
  6. data/bin/mill +1 -27
  7. data/lib/mill/command.rb +13 -0
  8. data/lib/mill/commands/build.rb +17 -0
  9. data/lib/mill/commands/check.rb +16 -0
  10. data/lib/mill/commands/diff.rb +18 -0
  11. data/lib/mill/commands/list.rb +20 -0
  12. data/lib/mill/commands/snapshot.rb +20 -0
  13. data/lib/mill/commands/tree.rb +17 -0
  14. data/lib/mill/commands/types.rb +18 -0
  15. data/lib/mill/commands/upload.rb +24 -0
  16. data/lib/mill/config.rb +27 -0
  17. data/lib/mill/resource.rb +81 -81
  18. data/lib/mill/resources/blob.rb +4 -0
  19. data/lib/mill/resources/feed.rb +5 -13
  20. data/lib/mill/resources/image.rb +10 -18
  21. data/lib/mill/resources/markdown.rb +20 -0
  22. data/lib/mill/resources/markup.rb +62 -0
  23. data/lib/mill/resources/page.rb +140 -0
  24. data/lib/mill/resources/redirect.rb +17 -16
  25. data/lib/mill/resources/robots.rb +3 -4
  26. data/lib/mill/resources/sitemap.rb +3 -5
  27. data/lib/mill/resources/stylesheet.rb +4 -4
  28. data/lib/mill/resources/textile.rb +27 -0
  29. data/lib/mill/resources.rb +89 -0
  30. data/lib/mill/site.rb +181 -251
  31. data/lib/mill.rb +28 -8
  32. data/mill.gemspec +22 -17
  33. data/test/content/a.md +3 -0
  34. data/test/content/b/ba.md +3 -0
  35. data/test/content/b/bb.md +3 -0
  36. data/test/content/b/index.md +3 -0
  37. data/test/content/c.md +4 -0
  38. data/test/content/d.md +4 -0
  39. data/test/content/index.md +3 -0
  40. data/test/main_test.rb +68 -0
  41. data/test/mill.yaml +8 -0
  42. metadata +189 -40
  43. data/TODO.txt +0 -61
  44. data/lib/mill/html_helpers.rb +0 -122
  45. data/lib/mill/navigator.rb +0 -61
  46. data/lib/mill/resources/dir.rb +0 -31
  47. data/lib/mill/resources/google_site_verification.rb +0 -30
  48. data/lib/mill/resources/text.rb +0 -207
  49. data/lib/mill/version.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 552fff29a5bcd1a558ba23332357186141369ddb3077452a0b0d8a7514df257a
4
- data.tar.gz: 96b5ce3171c004bcf807b3d8b6185bd83b3c8d120f5e896cf046ba8dca70a38f
3
+ metadata.gz: 72216a7795be9cfd8a2039e3eac35f73f4c129a30e951074c638d6c77088c869
4
+ data.tar.gz: aead637972f953d1d01aaee384554766361cfb4d9efa1ae95a6aaa2e2e5e6d11
5
5
  SHA512:
6
- metadata.gz: a8fb52dc75caeee17ba0fe74d1182b4a64c9dd6edaca7891360019e70facb3cac35cfdf46ad6a6c81708ef65fce775969b12d404c5d577ca922989a2b0155c01
7
- data.tar.gz: 12423dfe0681de5e5aacf90cb97167b44193e332ed7f8a62b1b4cbbb7426fe4d4ce3f5cde9784c414683cfb9b1096357bffd74bccb1dcaf1a2b4457bcd07ee5a
6
+ metadata.gz: 34d154f5a9cfa6e0a10d4abc57c7d3d99e18e857b1457e5ab9fbf82b50d426f469ed8cd79dc8821cdcddcf9e7fc95920d868b2025a0a328fdab9624e7d83d115
7
+ data.tar.gz: 8c433c5295ad53229ac23473eddd21b12995f225beb542e9c4d64ecafbec63d215fa343630e3380b5b162b62fd63c29eeba9b58f0404cf281aa89c36ef666bd2
data/.gitignore CHANGED
@@ -1 +1,11 @@
1
- *.gem
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ /test/output
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014 jslabovitz
3
+ Copyright (c) 2023 John S. Labovitz
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- require 'rubygems/tasks'
2
- Gem::Tasks.new
1
+ require 'simple-rake-tasks'
data/TODO.md ADDED
@@ -0,0 +1,4 @@
1
+ # TODO
2
+
3
+ - make Resources use tree for lookup instead of dictionary
4
+ - keep simple array of resources for quick access/iterators
data/bin/mill CHANGED
@@ -2,30 +2,4 @@
2
2
 
3
3
  require 'mill'
4
4
 
5
- load Path.pwd / 'code' / 'site.rb'
6
-
7
- begin
8
- case (command = ARGV.shift)
9
- when nil, 'make'
10
- $site.make
11
- when 'clean'
12
- $site.clean
13
- when 'check'
14
- $site.check
15
- when 'list'
16
- $site.list
17
- when 'diff'
18
- $site.diff
19
- when 'snapshot'
20
- $site.snapshot
21
- when 'upload'
22
- $site.upload
23
- when 'output-dir'
24
- puts $site.output_dir
25
- else
26
- raise "Unknown command: #{command.inspect}"
27
- end
28
- rescue Mill::Error => e
29
- warn e
30
- exit(1)
31
- end
5
+ Simple::CommandParser.run(ARGV)
@@ -0,0 +1,13 @@
1
+ module Mill
2
+
3
+ class Command < Simple::CommandParser::Command
4
+
5
+ attr_accessor :dir
6
+
7
+ def run(args)
8
+ @site = Mill::Site.load(@dir || '.')
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,17 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Build < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.build
10
+ @site.save
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Check < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.check
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,18 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Diff < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.output_dir.chdir do
10
+ run_command(%w[git diff])
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,20 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class List < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.build
10
+ @site.resources.each do |resource|
11
+ resource.print
12
+ puts
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Snapshot < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.output_dir.chdir do
10
+ run_command(%w[git init]) unless Path.new('.git').exist?
11
+ run_command(%w[git add .])
12
+ run_command(%w[git commit -a -m Update.])
13
+ end
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,17 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Tree < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.build
10
+ @site.resources.print_tree
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,18 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Types < Command
6
+
7
+ def run(args)
8
+ super
9
+ @site.file_types.sort.each do |type, klass|
10
+ puts '%-40s %s' % [type, klass]
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,24 @@
1
+ module Mill
2
+
3
+ module Commands
4
+
5
+ class Upload < Command
6
+
7
+ def run(args)
8
+ super
9
+ raise "site_rsync not defined" unless @site.site_rsync
10
+ options = %w[
11
+ --progress
12
+ --verbose
13
+ --archive
14
+ --exclude=.git
15
+ --delete-after
16
+ ]
17
+ run_command('rsync', *options, @site.output_dir, @site.site_rsync, verbose: true)
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,27 @@
1
+ module Mill
2
+
3
+ ConfigFileName = 'mill.yaml'
4
+
5
+ BaseConfig = Simple::Configurator.define(
6
+ dir: { default: '.', converter: :path },
7
+ input_dir: { default: 'content', converter: :path },
8
+ output_dir: { default: 'public_html', converter: :path },
9
+ code_dir: { default: 'code', converter: :path },
10
+ site_uri: { default: 'http://localhost', converter: :uri },
11
+ site_rsync: nil,
12
+ site_title: nil,
13
+ site_email: nil,
14
+ site_twitter: { converter: :uri },
15
+ site_instagram: { converter: :uri },
16
+ site_postal: nil,
17
+ site_phone: nil,
18
+ site_control_date: { converter: :date },
19
+ html_version: { default: :html5, converter: :symbol },
20
+ make_error: true,
21
+ make_feed: true,
22
+ make_sitemap: true,
23
+ make_robots: true,
24
+ allow_robots: true,
25
+ )
26
+
27
+ end
data/lib/mill/resource.rb CHANGED
@@ -4,110 +4,105 @@ module Mill
4
4
 
5
5
  FileTypes = []
6
6
 
7
- attr_accessor :input_file
8
- attr_accessor :output_file
7
+ attr_accessor :path
8
+ attr_accessor :uri
9
+ attr_accessor :primary
10
+ attr_accessor :input
9
11
  attr_accessor :date
10
- attr_accessor :public
11
- attr_accessor :content
12
+ attr_reader :output
12
13
  attr_accessor :site
14
+ attr_accessor :node
13
15
 
14
- def initialize(input_file: nil,
15
- output_file: nil,
16
- date: nil,
17
- public: false,
18
- content: nil,
19
- site: nil)
20
- if input_file
21
- @input_file = Path.new(input_file)
22
- @date = input_file.mtime.to_datetime
23
- else
24
- @date = DateTime.now
16
+ include SetParams
17
+ include Simple::Printer::Printable
18
+
19
+ def initialize(params={})
20
+ super({ primary: false }.merge(params))
21
+ unless defined?(@date)
22
+ @date = @input&.kind_of?(Path) ? @input.mtime.to_datetime : DateTime.now
25
23
  end
26
- @output_file = Path.new(output_file) if output_file
27
- self.date = date if date
28
- self.public = public
29
- @content = content
30
- @site = site
31
- end
32
-
33
- def date=(date)
34
- @date = case date
35
- when String, Time
36
- begin
37
- DateTime.parse(date.to_s)
38
- rescue ArgumentError => e
39
- raise Error, "Can't parse date: #{date.inspect}"
40
- end
41
- when Date, DateTime, nil
42
- date
24
+ @uri = Addressable::URI.encode(@path, Addressable::URI)
25
+ end
26
+
27
+ def inspect
28
+ "<#{self.class}>"
29
+ end
30
+
31
+ def primary?
32
+ @primary
33
+ end
34
+
35
+ def root?
36
+ self == @site.root_resource
37
+ end
38
+
39
+ def output_file
40
+ if @site && @path
41
+ @site.output_dir / Path.new(@path).relative_to('/')
43
42
  else
44
- raise Error, "Can't assign 'date' attribute: #{date.inspect}"
43
+ nil
45
44
  end
46
45
  end
47
46
 
48
- def public=(public)
49
- @public = case public
50
- when 'false', FalseClass
51
- false
52
- when 'true', TrueClass
53
- true
47
+ def printable
48
+ [
49
+ :path,
50
+ { key: :input, value: input_description },
51
+ { key: :output_file, value: (o = output_file) ? o.relative_to(@site.output_dir).to_s : nil },
52
+ :date,
53
+ :primary?,
54
+ :class,
55
+ { label: 'Parent', value: parent&.path || '-' },
56
+ { label: 'Siblings', value: siblings&.map(&:path)&.join(', ') || '-' },
57
+ { label: 'Children', value: children&.map(&:path)&.join(', ') || '-' },
58
+ ]
59
+ end
60
+
61
+ def input_description
62
+ case @input
63
+ when Path
64
+ @input.relative_to(@site.input_dir).to_s
65
+ when String
66
+ (@input[0...100] + '...').inspect
67
+ when nil
68
+ '-'
54
69
  else
55
- raise Error, "Can't assign 'public' attribute: #{public.inspect}"
70
+ "<#{@input.class}>"
56
71
  end
57
72
  end
58
73
 
59
- def public?
60
- @public
74
+ def parent
75
+ @node&.parent&.content
61
76
  end
62
77
 
63
- def inspect
64
- "<%p> input_file: %p, output_file: %p, date: %s, public: %p, content: <%p>" % [
65
- self.class,
66
- @input_file ? @input_file.relative_to(@site.input_dir).to_s : nil,
67
- @output_file ? @output_file.relative_to(@site.output_dir).to_s : nil,
68
- @date.to_s,
69
- @public,
70
- @content && @content.class,
71
- ]
78
+ def siblings
79
+ @node ? @node.siblings.map(&:content).compact : []
72
80
  end
73
81
 
74
- def find_sibling_resources(klass=nil)
75
- # parent_uri = parent_uri
76
- @site.resources.select do |resource|
77
- resource != self &&
78
- (klass.nil? || resource.kind_of?(klass)) &&
79
- resource.parent_uri == parent_uri
80
- end
82
+ def previous_sibling
83
+ @node&.previous_sibling&.content
81
84
  end
82
85
 
83
- def uri
84
- raise Error, "#{@input_file}: No output file defined for #{self.class}" unless @output_file
85
- path = '/' + @output_file.relative_to(@site.output_dir).to_s
86
- path.sub!(%r{/index\.html$}, '/')
87
- path.sub!(%r{\.html$}, '') if @site.shorten_uris
88
- Addressable::URI.encode(path, Addressable::URI)
86
+ def next_sibling
87
+ @node&.next_sibling&.content
89
88
  end
90
89
 
91
- def parent_uri
92
- uri + '.'
90
+ def children
91
+ @node ? @node.children.map(&:content).compact : []
93
92
  end
94
93
 
95
94
  def absolute_uri
96
- @site.site_uri + uri
95
+ @site.site_uri + @uri
97
96
  end
98
97
 
99
98
  def tag_uri
100
- @site.tag_uri + uri
99
+ @site.tag_uri + @uri
101
100
  end
102
101
 
103
102
  def change_frequency
104
103
  :weekly
105
104
  end
106
105
 
107
- def final_content
108
- @content
109
- end
110
-
111
106
  def load
112
107
  # implemented in subclass
113
108
  end
@@ -117,16 +112,21 @@ module Mill
117
112
  end
118
113
 
119
114
  def save
120
- @output_file.dirname.mkpath
121
- if (content = final_content)
122
- # ;;warn "#{uri}: writing #{@input_file} to #{@output_file}"
123
- @output_file.write(content.to_s)
124
- @output_file.utime(@date.to_time, @date.to_time)
125
- elsif @input_file
126
- # ;;warn "#{uri}: copying #{@input_file} to #{@output_file}"
127
- @input_file.copy(@output_file)
115
+ file = output_file
116
+ file.dirname.mkpath
117
+ if @output
118
+ # ;;warn "#{@path}: writing output to #{file}"
119
+ file.write(@output)
120
+ file.utime(@date.to_time, @date.to_time)
121
+ elsif @input.kind_of?(Path)
122
+ # ;;warn "#{@path}: copying #{@input} to #{file}"
123
+ @input.copy(file)
124
+ elsif @input
125
+ # ;;warn "#{@path}: writing input to #{file}"
126
+ file.write(@input)
127
+ file.utime(@date.to_time, @date.to_time)
128
128
  else
129
- raise Error, "Can't build resource without content or input file: #{uri}"
129
+ raise Error, "#{@path}: Can't build resource without output or input file"
130
130
  end
131
131
  end
132
132
 
@@ -5,6 +5,8 @@ module Mill
5
5
  class Blob < Resource
6
6
 
7
7
  FileTypes = %w{
8
+ text/plain
9
+
8
10
  application/pdf
9
11
 
10
12
  application/zip
@@ -16,9 +18,11 @@ module Mill
16
18
  application/x-javascript
17
19
 
18
20
  font/otf
21
+ font/woff2
19
22
  application/font-sfnt
20
23
  application/x-font-opentype
21
24
  application/x-font-otf
25
+ application/font-woff
22
26
 
23
27
  application/mp4
24
28
  audio/mpeg
@@ -1,20 +1,16 @@
1
- # see http://www.sitemaps.org/protocol.php
2
-
3
1
  module Mill
4
2
 
5
3
  class Resource
6
4
 
7
5
  class Feed < Resource
8
6
 
9
- include HTMLHelpers
10
-
11
7
  def build
12
8
  resources = @site.feed_resources
13
- builder = Nokogiri::XML::Builder.new do |xml|
9
+ @output = Nokogiri::XML::Builder.new do |xml|
14
10
  xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do
15
11
  xml.id(@site.tag_uri)
16
12
  xml.title(@site.site_title) if @site.site_title
17
- xml.link(rel: 'alternate', type: 'text/html', href: @site.home_resource.absolute_uri) if @site.home_resource
13
+ xml.link(rel: 'alternate', type: 'text/html', href: @site.root_resource.absolute_uri) if @site.root_resource
18
14
  xml.link(rel: 'self', type: 'application/atom+xml', href: absolute_uri)
19
15
  xml.author do
20
16
  xml.name(@site.feed_author_name) if @site.feed_author_name
@@ -35,15 +31,11 @@ module Mill
35
31
  end
36
32
  end
37
33
  end
38
- end
39
- @content = builder.doc
40
- super
34
+ end.doc
41
35
  end
42
36
 
43
- def link_html
44
- html_fragment do |html|
45
- html.link(href: uri, rel: 'alternate', type: 'application/atom+xml')
46
- end
37
+ def build_link(html)
38
+ html.link(href: uri, rel: 'alternate', type: 'application/atom+xml')
47
39
  end
48
40
 
49
41
  end
@@ -4,8 +4,6 @@ module Mill
4
4
 
5
5
  class Image < Resource
6
6
 
7
- include HTMLHelpers
8
-
9
7
  FileTypes = %w{
10
8
  image/gif
11
9
  image/jpeg
@@ -18,27 +16,21 @@ module Mill
18
16
  attr_accessor :width
19
17
  attr_accessor :height
20
18
 
21
- def inspect
22
- super + ", width: %p, height: %p" % [
23
- @width,
24
- @height,
19
+ def printable
20
+ super + [
21
+ :width,
22
+ :height,
25
23
  ]
26
24
  end
27
25
 
28
26
  def load
29
- info = ImageSize.path(@input_file.to_s)
30
- @width, @height = *info.size
31
- super
32
- end
33
-
34
- def img_html
35
- html_fragment do |html|
36
- html.img(
37
- src: uri,
38
- alt: @title,
39
- height: @height,
40
- width: @width)
27
+ raise Error, "Input must be file" unless @input.kind_of?(Path)
28
+ begin
29
+ info = ImageSize.path(@input.to_s)
30
+ rescue => e
31
+ raise Error, "Can't load image file #{@input.to_s.inspect}: #{e}"
41
32
  end
33
+ @width, @height = *info.size
42
34
  end
43
35
 
44
36
  end
@@ -0,0 +1,20 @@
1
+ module Mill
2
+
3
+ class Resource
4
+
5
+ class Markdown < Markup
6
+
7
+ FileTypes = %w{
8
+ text/markdown
9
+ text/x-web-markdown
10
+ }
11
+
12
+ def parse_text(text)
13
+ Kramdown::Document.new((text || '').strip).to_html
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,62 @@
1
+ module Mill
2
+
3
+ class Resource
4
+
5
+ class Markup < Resource
6
+
7
+ def printable
8
+ super + [
9
+ :header,
10
+ { label: 'Text', value: @text[0...100] + '...' },
11
+ ]
12
+ end
13
+
14
+ def load
15
+ @text = case @input
16
+ when Path
17
+ @input.read
18
+ when String
19
+ @input.dup
20
+ else
21
+ raise Error, "Unknown markup input: #{@input.class}"
22
+ end
23
+ @header = HashStruct.new
24
+ if @text.split(/\n/, 2).first =~ /^\w+:\s+/
25
+ fields = {}
26
+ lines, @text = @text.split(/\n\n/, 2)
27
+ lines.split(/\n/).each do |line|
28
+ if line.start_with?(/\s+/)
29
+ key = fields.keys.last
30
+ fields[key] += line
31
+ else
32
+ key, value = line.strip.split(/:\s*/, 2)
33
+ fields[key] = value
34
+ end
35
+ end
36
+ @header = HashStruct.new(fields)
37
+ end
38
+ end
39
+
40
+ def parse_text(text)
41
+ # implemented in subclass
42
+ end
43
+
44
+ def convert_class
45
+ @site.resource_class_for_type('text/html')
46
+ end
47
+
48
+ def convert
49
+ return nil if @header[:draft]
50
+ convert_class.new(
51
+ path: @path.sub(%r{\.\w+$}, '.html'),
52
+ input: parse_text(@text),
53
+ date: @date,
54
+ **@header,
55
+ )
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end