mill 0.16 → 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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/Rakefile +1 -9
  4. data/TODO.md +4 -0
  5. data/bin/mill +1 -27
  6. data/lib/mill/command.rb +13 -0
  7. data/lib/mill/commands/build.rb +17 -0
  8. data/lib/mill/commands/check.rb +16 -0
  9. data/lib/mill/commands/diff.rb +18 -0
  10. data/lib/mill/commands/list.rb +20 -0
  11. data/lib/mill/commands/snapshot.rb +20 -0
  12. data/lib/mill/commands/tree.rb +17 -0
  13. data/lib/mill/commands/types.rb +18 -0
  14. data/lib/mill/commands/upload.rb +24 -0
  15. data/lib/mill/config.rb +27 -0
  16. data/lib/mill/resource.rb +74 -87
  17. data/lib/mill/resources/blob.rb +4 -0
  18. data/lib/mill/resources/feed.rb +5 -13
  19. data/lib/mill/resources/image.rb +10 -18
  20. data/lib/mill/resources/markdown.rb +20 -0
  21. data/lib/mill/resources/markup.rb +62 -0
  22. data/lib/mill/resources/page.rb +140 -0
  23. data/lib/mill/resources/redirect.rb +17 -16
  24. data/lib/mill/resources/robots.rb +3 -4
  25. data/lib/mill/resources/sitemap.rb +3 -5
  26. data/lib/mill/resources/stylesheet.rb +4 -4
  27. data/lib/mill/resources/textile.rb +27 -0
  28. data/lib/mill/resources.rb +89 -0
  29. data/lib/mill/site.rb +182 -283
  30. data/lib/mill.rb +27 -9
  31. data/mill.gemspec +19 -19
  32. data/test/content/c.md +4 -0
  33. data/test/content/d.md +4 -0
  34. data/test/main_test.rb +68 -0
  35. data/test/mill.yaml +8 -0
  36. metadata +118 -51
  37. data/Gemfile +0 -6
  38. data/TODO.txt +0 -61
  39. data/lib/mill/html_helpers.rb +0 -128
  40. data/lib/mill/navigator.rb +0 -61
  41. data/lib/mill/resources/dir.rb +0 -31
  42. data/lib/mill/resources/google_site_verification.rb +0 -30
  43. data/lib/mill/resources/text.rb +0 -218
  44. data/lib/mill/version.rb +0 -5
  45. data/test/test.rb +0 -56
@@ -1,128 +0,0 @@
1
- module HTMLHelpers
2
-
3
- LinkElementsXPath = '//@href | //@src'
4
-
5
- def html_document(type=:html4_transitional, &block)
6
- doc = Nokogiri::HTML::Document.new
7
- doc.encoding = 'UTF-8'
8
- doc.internal_subset.remove
9
- case type
10
- when :html4_transitional
11
- doc.create_internal_subset('html', '-//W3C//DTD HTML 4.01 Transitional//EN', 'http://www.w3.org/TR/html4/loose.dtd')
12
- when :html5
13
- doc.create_internal_subset('html', nil, nil)
14
- else
15
- raise "Unknown HTML type: #{type.inspect}"
16
- end
17
- Nokogiri::HTML::Builder.with(doc) do |doc|
18
- yield(doc)
19
- end
20
- doc
21
- end
22
-
23
- def html_fragment(&block)
24
- html = Nokogiri::HTML::DocumentFragment.parse('')
25
- Nokogiri::HTML::Builder.with(html) do |html|
26
- yield(html) if block_given?
27
- end
28
- html
29
- end
30
-
31
- def parse_html(str)
32
- if str.strip.empty?
33
- html = html_fragment
34
- else
35
- html = Nokogiri::HTML::Document.parse(str) { |config| config.strict }
36
- check_errors(html)
37
- end
38
- html
39
- end
40
-
41
- def parse_html_fragment(str)
42
- html = Nokogiri::HTML::DocumentFragment.parse(str) { |config| config.strict }
43
- check_errors(html)
44
- html
45
- end
46
-
47
- def check_errors(html)
48
- html.errors.each do |error|
49
- raise Mill::Error, "HTML error #{error}" unless error.message =~ /Tag .+? invalid$/
50
- end
51
- end
52
-
53
- def find_link_elements(html)
54
- html.xpath(LinkElementsXPath)
55
- end
56
-
57
- def replace_element(html, xpath, &block)
58
- html.xpath(xpath).each do |elem|
59
- elem.replace(yield(elem))
60
- end
61
- end
62
-
63
- def google_analytics(tracker_id)
64
- html_fragment do |html|
65
- html.script(type: 'text/javascript') do
66
- html << %Q{
67
- var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
68
- document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
69
- }
70
- end
71
- html.script(type: 'text/javascript') do
72
- html << %Q{
73
- try {
74
- var pageTracker = _gat._getTracker("#{tracker_id}");
75
- pageTracker._trackPageview();
76
- } catch(err) {}
77
- }
78
- end
79
- end
80
- end
81
-
82
- def link_if(state, html, &block)
83
- elem = html_fragment { |h| yield(h) }
84
- if state
85
- html.a(href: uri) { html << elem.to_html }
86
- else
87
- html << elem.to_html
88
- end
89
- end
90
-
91
- class PreText < String
92
-
93
- def to_html
94
- html_fragment do |html|
95
- html.pre(self)
96
- end
97
- end
98
-
99
- end
100
-
101
- class ::String
102
-
103
- Converters = {
104
- nil => RubyPants,
105
- smart_quotes: RubyPants,
106
- markdown: Kramdown::Document,
107
- textile: [RedCloth, :no_span_caps],
108
- pre: PreText,
109
- }
110
-
111
- def to_html(options={})
112
- converter_class = Converters[options[:mode]] or raise "Unknown to_html mode: #{options[:mode].inspect}"
113
- if converter_class.kind_of?(Array)
114
- converter_class, *converter_options = *converter_class
115
- converter = converter_class.new(self, converter_options)
116
- else
117
- converter = converter_class.new(self)
118
- end
119
- html = Nokogiri::HTML::DocumentFragment.parse(converter.to_html)
120
- if !options[:multiline] && (p_elem = html.at_xpath('p'))
121
- html = p_elem.children.to_html
122
- end
123
- html.to_html
124
- end
125
-
126
- end
127
-
128
- end
@@ -1,61 +0,0 @@
1
- module Mill
2
-
3
- class Navigator
4
-
5
- class Item
6
-
7
- attr_accessor :uri
8
- attr_accessor :title
9
-
10
- def initialize(uri:, title: nil)
11
- @uri = Addressable::URI.parse(uri)
12
- @title = title
13
- end
14
-
15
- end
16
-
17
- def initialize(items: [], site: nil)
18
- @items = Hash[
19
- items.map do |uri, title|
20
- item = Item.new(uri: uri, title: title)
21
- [item.uri, item]
22
- end
23
- ]
24
- @site = site
25
- end
26
-
27
- def items
28
- @items.values
29
- end
30
-
31
- def first_item
32
- @items.values.first
33
- end
34
-
35
- def last_item
36
- @items.values.last
37
- end
38
-
39
- def previous_item(uri)
40
- if (item = @items[uri])
41
- i = @items.values.index(item)
42
- if i > 0
43
- return @items.values[i - 1]
44
- end
45
- end
46
- nil
47
- end
48
-
49
- def next_item(uri)
50
- if (item = @items[uri])
51
- i = @items.values.index(item)
52
- if i < @items.length - 1
53
- return @items.values[i + 1]
54
- end
55
- end
56
- nil
57
- end
58
-
59
- end
60
-
61
- end
@@ -1,31 +0,0 @@
1
- module Mill
2
-
3
- class Resource
4
-
5
- class Index < Text
6
-
7
- include HTMLHelpers
8
-
9
- attr_accessor :pages
10
-
11
- def initialize(pages:, **args)
12
- @pages = pages
13
- super(**args)
14
- end
15
-
16
- def load
17
- super
18
- @content = html_fragment do |html|
19
- html.dl do
20
- @pages.each do |page|
21
- html.dt(page.title)
22
- end
23
- end
24
- end
25
- end
26
-
27
- end
28
-
29
- end
30
-
31
- end
@@ -1,30 +0,0 @@
1
- module Mill
2
-
3
- class Resource
4
-
5
- class GoogleSiteVerification < Resource
6
-
7
- attr_accessor :key
8
-
9
- def initialize(key:, **args)
10
- @key = key
11
- @public = false
12
- super(**args)
13
- end
14
-
15
- def inspect
16
- super + ", key: %p" % [
17
- @key,
18
- ]
19
- end
20
-
21
- def load
22
- @content = "google-site-verification: #{@key}.html\n"
23
- super
24
- end
25
-
26
- end
27
-
28
- end
29
-
30
- end
@@ -1,218 +0,0 @@
1
-
2
- module Mill
3
-
4
- class Resource
5
-
6
- class Text < Resource
7
-
8
- include HTMLHelpers
9
-
10
- FileTypes = %w{
11
- text/plain
12
- text/html
13
- text/markdown
14
- }
15
-
16
- attr_accessor :title
17
- attr_accessor :summary
18
- attr_accessor :author
19
- attr_accessor :type
20
-
21
- def initialize(title: nil, summary: nil, author: nil, public: true, output_file: nil, **args)
22
- @title = title
23
- @summary = summary
24
- @author = author
25
- @type = nil
26
- super(
27
- public: public,
28
- output_file: output_file&.replace_extension('.html'),
29
- **args)
30
- if @path
31
- @path.sub!(%r{\.html$}, '') if @site&.shorten_uris
32
- @path.sub!(%r{(.*)index$}, '\1')
33
- end
34
- end
35
-
36
- def inspect
37
- super + ", title: %p, summary: %p, author: %p, type: %p" % [
38
- @title,
39
- @summary,
40
- @author,
41
- @type,
42
- ]
43
- end
44
-
45
- def load
46
- super
47
- if @input_file
48
- @content = @input_file.read
49
- @type = case @input_file.extname.downcase
50
- when '.md', '.mdown', '.markdown'
51
- :markdown
52
- when '.textile'
53
- :textile
54
- when '.txt'
55
- :pre
56
- when '.htm', '.html'
57
- :html
58
- else
59
- raise "Unknown text type: #{@input_file}"
60
- end
61
- if @type != :html
62
- parse_text_header
63
- @content = (@content || '').to_html(mode: @type, multiline: true)
64
- @type = :html
65
- end
66
- begin
67
- @content = parse_html(@content)
68
- rescue Error => e
69
- raise e, "#{@input_file}: #{e}"
70
- end
71
- parse_html_header
72
- end
73
- end
74
-
75
- def parse_html_header
76
- unless @title
77
- if (title_elem = @content.at_xpath('/html/head/title'))
78
- @title = title_elem.text
79
- else
80
- @title = @path
81
- end
82
- end
83
- @content.xpath('/html/head/meta[@name]').each do |meta|
84
- send("#{meta['name']}=", meta['content'])
85
- end
86
- end
87
-
88
- def parse_text_header
89
- if @content.split(/\n/, 2).first =~ /^\w+:\s+/
90
- header, @content = @content.split(/\n\n/, 2)
91
- header.split(/\n/).map do |line|
92
- key, value = line.strip.split(/:\s*/, 2)
93
- key = key.gsub('-', '_').downcase.to_sym
94
- send("#{key}=", value)
95
- end
96
- end
97
- end
98
-
99
- def final_content
100
- html_document(@site&.html_version || :html5) do |doc|
101
- doc.html(lang: 'en') do |html|
102
- html.parent << head
103
- html.parent << body
104
- end
105
- end.to_html
106
- end
107
-
108
- def head(&block)
109
- html_fragment do |html|
110
- html.head do
111
- head = content_head
112
- if (title = @title || (head && head.at_xpath('title')))
113
- html.title { html << title.to_html }
114
- end
115
- yield(html) if block_given?
116
- if head
117
- head.children.reject { |e| e.text? || e.name == 'title' }.each do |e|
118
- html << e.to_html
119
- end
120
- end
121
- end
122
- end
123
- end
124
-
125
- def body(&block)
126
- html_fragment do |html|
127
- html.body do
128
- if (elem = content_body)
129
- html << elem.children.to_html
130
- end
131
- yield(html) if block_given?
132
- end
133
- end
134
- end
135
-
136
- def content_head
137
- @content && @content.at_xpath('/html/head')
138
- end
139
-
140
- def content_body
141
- @content && @content.at_xpath('/html/body')
142
- end
143
-
144
- def add_external_link_targets(rel='noopener')
145
- @content.xpath('//a').each do |a|
146
- if a['href'] && a['href'] =~ /^\w+:/
147
- a['target'] = '_blank'
148
- a['rel'] = rel
149
- end
150
- end
151
- end
152
-
153
- def remove_comments
154
- @content.xpath('//comment()').each do |comment|
155
- comment.remove
156
- end
157
- end
158
-
159
- def add_image_sizes
160
- @content.xpath('//img').each do |img|
161
- # skip elements that already have width/height defined
162
- next if img[:width] || img[:height]
163
- img_link = Addressable::URI.parse(img['src'])
164
- raise Error, "No link in <img> element: #{img.to_s}" if img_link.nil? || img_link.empty?
165
- next if img_link.host
166
- img_uri = uri + img_link
167
- img_resource = @site.find_resource(img_uri) or raise Error, "Can't find image for #{img_uri}"
168
- img[:width], img[:height] = img_resource.width, img_resource.height
169
- end
170
- end
171
-
172
- def shorten_links
173
- find_link_elements(@content).each do |attribute|
174
- elem = attribute.parent
175
- link_uri = Addressable::URI.parse(attribute.value) or raise Error, "Can't parse #{attribute.value.inspect} from #{xpath.inspect}"
176
- link_uri = uri + link_uri
177
- if link_uri.relative?
178
- self_uri = uri.normalize
179
- self_uri.scheme = 'http'
180
- link_uri.scheme = 'http'
181
- attribute.value = self_uri.route_to(link_uri)
182
- # ;;warn "[#{path}] shortened link #{elem.name}/@#{attribute.name}: #{link_uri} => #{attribute.value}"
183
- end
184
- end
185
- end
186
-
187
- def summary
188
- @summary || ((p = feed_content.at_xpath('//p')) && p.children)
189
- end
190
-
191
- def feed_content
192
- if (body = content_body)
193
- # If we have a main element (<div class="main"> or <main>), use that.
194
- # Otherwise, use the body, but delete header/footer/nav divs or elements.
195
- if (main = body.at_xpath('//div[@id="main"]')) || (main = body.at_xpath('//main'))
196
- main.children
197
- elsif (article = body.at_xpath('//article'))
198
- article.children
199
- else
200
- body_elem = body.clone
201
- %w{header nav masthead footer}.each do |name|
202
- if (elem = body_elem.at_xpath("//div[@id=\"#{name}\"]")) || (elem = body_elem.at_xpath("//#{name}"))
203
- elem.remove
204
- end
205
- end
206
- body_elem.children
207
- end
208
- else
209
- warn "Warning: Resource #{path} (#{self.class}) has no content"
210
- nil
211
- end
212
- end
213
-
214
- end
215
-
216
- end
217
-
218
- end
data/lib/mill/version.rb DELETED
@@ -1,5 +0,0 @@
1
- module Mill
2
-
3
- VERSION = '0.16'
4
-
5
- end
data/test/test.rb DELETED
@@ -1,56 +0,0 @@
1
- $VERBOSE = false
2
-
3
- require 'minitest/autorun'
4
- require 'minitest/power_assert'
5
-
6
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
7
- require 'mill'
8
-
9
- module Mill
10
-
11
- class Test < Minitest::Test
12
-
13
- def setup
14
- @site = Site.new(
15
- input_dir: 'test/content',
16
- output_dir: 'test/output',
17
- site_title: 'Test',
18
- site_uri: 'http://test.test',
19
- html_version: :html5,
20
- )
21
- @site.make
22
- # ;;@site.print_tree
23
- # ;;@site.list
24
- # ;;binding.pry
25
- @root = @site.find_resource('/') or raise
26
- @a = @site.find_resource('/a') or raise
27
- @b = @site.find_resource('/b') or raise
28
- @ba = @site.find_resource('/b/ba') or raise
29
- @bb = @site.find_resource('/b/bb') or raise
30
- end
31
-
32
- def test_has_index
33
- assert { @root }
34
- end
35
-
36
- def test_children
37
- assert { @root.children == [@a, @b] }
38
- assert { @a.children.empty? }
39
- end
40
-
41
- def test_parent
42
- assert { @a.parent == @root }
43
- assert { @b.parent == @root }
44
- assert { @ba.parent == @b }
45
- assert { @bb.parent == @b }
46
- end
47
-
48
- def test_siblings
49
- assert { @root.siblings.empty? }
50
- assert { @a.siblings == [@b] }
51
- assert { @ba.siblings == [@bb] }
52
- end
53
-
54
- end
55
-
56
- end