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
@@ -0,0 +1,140 @@
1
+ module Mill
2
+
3
+ class Resource
4
+
5
+ class Page < Resource
6
+
7
+ FileTypes = %w{
8
+ text/html
9
+ }
10
+
11
+ attr_accessor :title
12
+ attr_reader :document
13
+
14
+ def initialize(params={})
15
+ super({ primary: true }.merge(params))
16
+ @path.sub!(%r{\.\w+$}, '')
17
+ @path.sub!(%r{/index$}, '/')
18
+ @uri = Addressable::URI.encode(@path, Addressable::URI)
19
+ end
20
+
21
+ def printable
22
+ super + [
23
+ :title,
24
+ ]
25
+ end
26
+
27
+ def output_file
28
+ if (file = super)
29
+ file /= 'index' if @path.end_with?('/')
30
+ file.add_extension('.html')
31
+ end
32
+ end
33
+
34
+ def load
35
+ @document = case @input
36
+ when Path
37
+ Simple::Builder.parse_html_document(@input.read)
38
+ when String
39
+ Simple::Builder.parse_html_document(@input)
40
+ when Nokogiri::HTML4::Document
41
+ @input
42
+ else
43
+ raise Error, "Unknown HTML input: #{@input.class}"
44
+ end
45
+ @title ||= @document.at_xpath('//h1')&.text || Simple::Builder.find_title_element(@document)&.text
46
+ set(Simple::Builder.find_meta_info(@document))
47
+ end
48
+
49
+ def build
50
+ raise Error, "No document defined" unless @document
51
+ builder = case @site.html_version
52
+ when :html4
53
+ :build_html4_document
54
+ when :html5, nil
55
+ :build_html5_document
56
+ else
57
+ raise "Unknown HTML version: #{@site.html_version.inspect}"
58
+ end
59
+ @output = Simple::Builder.send(builder) do |html|
60
+ html.html(lang: 'en') do
61
+ html.head do
62
+ build_head(html)
63
+ end
64
+ html.body do
65
+ build_body(html)
66
+ end
67
+ end
68
+ end.to_html
69
+ end
70
+
71
+ def build_head(html)
72
+ title = @title || Simple::Builder.find_title_element(@document)&.text
73
+ html.title { html << title.to_html } if title
74
+ if (head = document_head&.children)
75
+ head.reject { |e| e.text? || e.name == 'title' }.each do |e|
76
+ html << e.to_html
77
+ end
78
+ end
79
+ end
80
+
81
+ def build_body(html)
82
+ if (elem = document_body&.children)
83
+ html << elem.to_html
84
+ end
85
+ end
86
+
87
+ def document_head
88
+ Simple::Builder.find_head_element(@document)
89
+ end
90
+
91
+ def document_body
92
+ Simple::Builder.find_body_element(@document)
93
+ end
94
+
95
+ def add_image_sizes
96
+ @document.xpath('//img').each do |img|
97
+ # skip elements that already have width/height defined
98
+ next if img[:width] || img[:height]
99
+ img_link = Addressable::URI.parse(img['src'])
100
+ raise Error, "No link in <img> element: #{img.to_s}" if img_link.nil? || img_link.empty?
101
+ next if img_link.host
102
+ img_uri = @uri + img_link
103
+ img_resource = @site.find_resource(img_uri) or raise Error, "Can't find image for #{img_uri}"
104
+ img[:width], img[:height] = img_resource.width, img_resource.height
105
+ end
106
+ end
107
+
108
+ def shorten_links
109
+ Simple::Builder.find_link_element_attributes(@document).each do |attribute|
110
+ link_uri = Addressable::URI.parse(attribute.value) or raise Error, "Can't parse attribute value: #{attribute.inspect}"
111
+ link_uri = @uri + link_uri
112
+ if link_uri.relative?
113
+ self_uri = @uri.normalize
114
+ self_uri.scheme = link_uri.scheme = @site.site_uri.scheme
115
+ attribute.value = self_uri.route_to(link_uri)
116
+ # ;;warn "[#{@path}] shortened link #{attribute.parent.name}/@#{attribute.name}: #{link_uri} => #{attribute.value}"
117
+ end
118
+ end
119
+ end
120
+
121
+ def feed_content
122
+ document_body&.children
123
+ end
124
+
125
+ def build_link(html)
126
+ html.a(href: @uri) { html << (@title || @path).to_html }
127
+ end
128
+
129
+ def links
130
+ raise "#{@path}: Can't get links for empty document: #{self}" unless @document
131
+ Simple::Builder.find_link_element_attributes(@document).map do |u|
132
+ (@uri + u).normalize
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -4,30 +4,31 @@ module Mill
4
4
 
5
5
  class Redirect < Resource
6
6
 
7
- attr_accessor :redirect_uri
7
+ attr_reader :redirect_uri
8
8
  attr_accessor :redirect_code
9
9
 
10
- def initialize(redirect_uri:, redirect_code: 303, **args)
11
- @redirect_uri = redirect_uri
12
- @redirect_code = redirect_code
13
- super(**args)
10
+ def initialize(redirect_uri:, redirect_code: nil, **params)
11
+ super(
12
+ {
13
+ redirect_uri: redirect_uri,
14
+ redirect_code: redirect_code || 303,
15
+ }.merge(params)
16
+ )
14
17
  end
15
18
 
16
- def inspect
17
- super + ", redirect_uri: %p, redirect_code: %p" % [
18
- @redirect_uri.to_s,
19
- @redirect_code,
20
- ]
19
+ def redirect_uri=(uri)
20
+ @redirect_uri = Addressable::URI.parse(uri)
21
21
  end
22
22
 
23
- def load
24
- @content = "%s %d" % [@redirect_uri, @redirect_code]
25
- super
23
+ def printable
24
+ super + [
25
+ :redirect_uri,
26
+ :redirect_code,
27
+ ]
26
28
  end
27
29
 
28
- def save
29
- @output_file = @output_file.add_extension('.redirect')
30
- super
30
+ def build
31
+ @output = "%s %d" % [@redirect_uri, @redirect_code]
31
32
  end
32
33
 
33
34
  end
@@ -9,10 +9,9 @@ module Mill
9
9
  def build
10
10
  info = {}
11
11
  info['User-Agent'] = '*'
12
- info['Disallow'] = '/' unless @site.allow_robots
13
- info['Sitemap'] = @site.sitemap_resource.absolute_uri if @site.make_sitemap
14
- @content = info.map { |key, value| "#{key}: #{value}" }.join("\n")
15
- super
12
+ info['Disallow'] = '/' unless @site.allow_robots?
13
+ info['Sitemap'] = @site.sitemap_resource.absolute_uri if @site.make_sitemap?
14
+ @output = info.map { |key, value| "#{key}: #{value}" }.join("\n")
16
15
  end
17
16
 
18
17
  end
@@ -7,11 +7,11 @@ module Mill
7
7
  class Sitemap < Resource
8
8
 
9
9
  def build
10
- builder = Nokogiri::XML::Builder.new do |xml|
10
+ @output = Nokogiri::XML::Builder.new do |xml|
11
11
  xml.urlset('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
12
12
  'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
13
13
  'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd') do
14
- @site.public_resources.each do |resource|
14
+ @site.sitemap_resources.each do |resource|
15
15
  xml.url do
16
16
  xml.loc(resource.absolute_uri)
17
17
  xml.lastmod(resource.date.iso8601)
@@ -19,9 +19,7 @@ module Mill
19
19
  end
20
20
  end
21
21
  end
22
- end
23
- @content = builder.doc
24
- super
22
+ end.doc
25
23
  end
26
24
 
27
25
  end
@@ -9,12 +9,12 @@ module Mill
9
9
  }
10
10
 
11
11
  def load
12
- super
13
- unless @input_file.basename.to_s.end_with?('.min.css')
12
+ raise Error, "Input must be file" unless @input.kind_of?(Path)
13
+ unless @input.basename.to_s.end_with?('.min.css')
14
14
  begin
15
- @content = SassC::Engine.new(@input_file.read, syntax: :scss, style: :compressed).render
15
+ @output = SassC::Engine.new(@input.read, syntax: :scss, style: :compressed).render
16
16
  rescue SassC::SyntaxError => e
17
- raise "#{input_file}: error parsing CSS: #{e}"
17
+ raise "#{@input}: error parsing CSS: #{e}"
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,27 @@
1
+ module Mill
2
+
3
+ class Resource
4
+
5
+ class Textile < Markup
6
+
7
+ FileTypes = %w{
8
+ text/textile
9
+ }
10
+
11
+ if MIME::Types['text/textile'].empty?
12
+ type = MIME::Type.new(
13
+ 'content-type' => 'text/textile',
14
+ 'preferred-extension' => 'textile',
15
+ )
16
+ MIME::Types.add(type)
17
+ end
18
+
19
+ def parse_text(text)
20
+ RedCloth.new((text || '').strip, [:no_span_caps]).to_html
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,89 @@
1
+ module Mill
2
+
3
+ class Resources
4
+
5
+ include SetParams
6
+
7
+ def initialize(params={})
8
+ super
9
+ @dictionary = {}
10
+ @tree = Tree::TreeNode.new('')
11
+ end
12
+
13
+ def <<(resource)
14
+ @dictionary[resource.path] = resource
15
+ if resource.primary?
16
+ node = find_or_create_node(resource.path)
17
+ resource.node = node
18
+ node.content = resource
19
+ end
20
+ end
21
+
22
+ def empty?
23
+ @dictionary.empty?
24
+ end
25
+
26
+ def [](path)
27
+ path = path.path if path.kind_of?(Addressable::URI)
28
+ @dictionary[path] || @dictionary[path + '/']
29
+ end
30
+
31
+ def each(&block)
32
+ @dictionary.values.each(&block)
33
+ end
34
+
35
+ def select(&block)
36
+ @dictionary.values.select(&block)
37
+ end
38
+
39
+ def of_class(klass)
40
+ @dictionary.values.select { |r| r.kind_of?(klass) }
41
+ end
42
+
43
+ def delete(resource)
44
+ @dictionary.delete(resource.path) or raise Error, "No resource with path #{resource.path.inspect}"
45
+ if (node = find_node(resource.path))
46
+ node.delete
47
+ end
48
+ end
49
+
50
+ def find_node(path)
51
+ node = @tree
52
+ path_components(path).each do |component|
53
+ node = node[component] or return nil
54
+ end
55
+ node
56
+ end
57
+
58
+ def find_or_create_node(path)
59
+ node = @tree
60
+ path_components(path).each do |component|
61
+ node = node[component] || (node << Tree::TreeNode.new(component))
62
+ end
63
+ node
64
+ end
65
+
66
+ def path_components(path)
67
+ path.split('/').reject(&:empty?)
68
+ end
69
+
70
+ def print_tree
71
+ print_node(@tree)
72
+ end
73
+
74
+ def print_node(node, level=0)
75
+ if node.is_root?
76
+ print '*'
77
+ else
78
+ print "\t" * level
79
+ end
80
+ print " #{node.name.inspect}"
81
+ print " <#{node.content&.path}>"
82
+ print " (#{node.children.length} children)" if node.has_children?
83
+ puts
84
+ node.children { |child| print_node(child, level + 1) }
85
+ end
86
+
87
+ end
88
+
89
+ end