mill 0.1

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.
@@ -0,0 +1,15 @@
1
+ class Mill
2
+
3
+ class Resource
4
+
5
+ class Generic < Resource
6
+
7
+ def self.type
8
+ :generic
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,36 @@
1
+ class Mill
2
+
3
+ class Resource
4
+
5
+ class Image < Resource
6
+
7
+ include HTMLHelpers
8
+
9
+ attr_accessor :width
10
+ attr_accessor :height
11
+
12
+ def self.type
13
+ :image
14
+ end
15
+
16
+ def load
17
+ info = ImageSize.path(@input_file.to_s)
18
+ @width, @height = *info.size
19
+ super
20
+ end
21
+
22
+ def img_html
23
+ html_fragment do |html|
24
+ html.img(
25
+ src: uri,
26
+ alt: @title,
27
+ height: @height,
28
+ width: @width)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,36 @@
1
+ class Mill
2
+
3
+ class Resource
4
+
5
+ class Redirect < Resource
6
+
7
+ attr_accessor :redirect_uri
8
+ attr_accessor :redirect_code
9
+
10
+ def self.type
11
+ :redirect
12
+ end
13
+
14
+ def initialize(params={})
15
+ super(
16
+ {
17
+ redirect_code: 303,
18
+ }.merge(params)
19
+ )
20
+ end
21
+
22
+ def load
23
+ @content = "%s %d" % [@redirect_uri, @redirect_code]
24
+ super
25
+ end
26
+
27
+ def build
28
+ @output_file = @output_file.add_extension('.redirect')
29
+ super
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,25 @@
1
+ # see http://www.robotstxt.org/robotstxt.html
2
+
3
+ class Mill
4
+
5
+ class Resource
6
+
7
+ class Robots < Resource
8
+
9
+ def self.type
10
+ :robots
11
+ end
12
+
13
+ def load
14
+ info = {
15
+ 'Sitemap' => @mill.sitemap_resource.absolute_uri,
16
+ }
17
+ @content = info.map { |key, value| "#{key}: #{value}" }.join("\n")
18
+ super
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,35 @@
1
+ # see http://www.sitemaps.org/protocol.php
2
+
3
+ class Mill
4
+
5
+ class Resource
6
+
7
+ class Sitemap < Resource
8
+
9
+ def self.type
10
+ :sitemap
11
+ end
12
+
13
+ def load
14
+ builder = Nokogiri::XML::Builder.new do |xml|
15
+ xml.urlset('xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
16
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
17
+ 'xsi:schemaLocation' => 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd') do
18
+ @mill.public_resources.each do |resource|
19
+ xml.url do
20
+ xml.loc(resource.absolute_uri)
21
+ xml.lastmod(resource.date.iso8601)
22
+ xml.changefreq(resource.change_frequency.to_s)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ @content = builder.doc
28
+ super
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,157 @@
1
+ class Mill
2
+
3
+ class Resource
4
+
5
+ class Text < Resource
6
+
7
+ include HTMLHelpers
8
+
9
+ attr_accessor :title
10
+
11
+ def self.type
12
+ :text
13
+ end
14
+
15
+ def initialize(params={})
16
+ super(
17
+ {
18
+ public: true,
19
+ }.merge(params)
20
+ )
21
+ end
22
+
23
+ def load
24
+ if @input_file
25
+ @content = @input_file.read
26
+ markup_class = case @input_file.extname
27
+ when '.md', '.mdown', '.markdown'
28
+ Kramdown::Document
29
+ when '.textile'
30
+ RedCloth
31
+ when '.txt'
32
+ PreText
33
+ else
34
+ nil
35
+ end
36
+ if markup_class
37
+ parse_text_header
38
+ raise "#{uri}: Content is empty" unless @content
39
+ @content = markup_class.new(@content).to_html
40
+ @output_file = @output_file.replace_extension('.html')
41
+ end
42
+ begin
43
+ @content = parse_html(@content)
44
+ rescue HTMLError => e
45
+ raise "failed to parse #{@input_file}: #{e}"
46
+ end
47
+ parse_html_header
48
+ end
49
+ add_image_sizes
50
+ convert_relative_links
51
+ super
52
+ end
53
+
54
+ def parse_html_header
55
+ if (title_elem = @content.at_xpath('/html/head/title'))
56
+ @title = title_elem.text
57
+ end
58
+ @content.xpath('/html/head/meta[@name]').each do |meta|
59
+ send("#{meta['name']}=", meta['content'])
60
+ end
61
+ end
62
+
63
+ def parse_text_header
64
+ if @content =~ /^\w+:\s+/
65
+ header, @content = @content.split(/\n\n/, 2)
66
+ header.split(/\n/).map do |line|
67
+ key, value = line.strip.split(/:\s+/, 2)
68
+ key = key.gsub('-', '_').downcase.to_sym
69
+ send("#{key}=", value)
70
+ end
71
+ end
72
+ end
73
+
74
+ def final_content
75
+ html_document do |doc|
76
+ doc.html(lang: 'en') do |html|
77
+ html.head do
78
+ html << head.to_html
79
+ end
80
+ html.body do
81
+ html << body.to_html
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def head
88
+ @content.at_xpath('/html/head').children
89
+ end
90
+
91
+ def body
92
+ @content.at_xpath('/html/body').children
93
+ end
94
+
95
+ def verify
96
+ tidy_html(@output_file.read) do |error_str|
97
+ warn "#{uri}: #{error_str}"
98
+ end
99
+ end
100
+
101
+ def add_external_link_targets
102
+ @content.xpath('//a').each do |a|
103
+ if a['href'] && a['href'] =~ /^\w+:/
104
+ a['target'] = '_blank'
105
+ end
106
+ end
107
+ end
108
+
109
+ def add_image_sizes
110
+ @content.xpath('//img').each do |img|
111
+ # skip elements that already have width/height defined
112
+ next if img[:width] || img[:height]
113
+ img_link = Addressable::URI.parse(img['src'])
114
+ raise "no link in <img> element: #{img.to_s}" if img_link.nil? || img_link.empty?
115
+ next if img_link.host
116
+ img_uri = uri + img_link
117
+ img_resource = @mill.find_resource(img_uri) or raise "Can't find image for #{img_uri}"
118
+ img[:width], img[:height] = img_resource.width, img_resource.height
119
+ end
120
+ end
121
+
122
+ def convert_relative_links
123
+ @mill.link_elem_attrs.each do |xpath|
124
+ convert_relative_link(xpath)
125
+ end
126
+ end
127
+
128
+ def convert_relative_link(elem_attr)
129
+ @content.xpath("//#{elem_attr}").each do |attribute|
130
+ elem = attribute.parent
131
+ link_uri = Addressable::URI.parse(attribute.value) or raise "Can't parse #{attribute.value.inspect} from #{xpath.inspect}"
132
+ if !link_uri.path.empty? && link_uri.path[0] != '/'
133
+ attribute.value = uri + link_uri
134
+ # ;;warn "[#{uri}] absolutized #{elem.name}/@#{attribute.name}: #{link_uri} => #{attribute.value}"
135
+ end
136
+ end
137
+ end
138
+
139
+ def feed_summary
140
+ ;;raise "#{uri} has no content" unless @content
141
+ if (p = @content.at_xpath('/html/body/p[1]'))
142
+ ['html', p.to_html]
143
+ else
144
+ nil
145
+ end
146
+ end
147
+
148
+ def feed_content
149
+ body = @content.at_xpath('/html/body') or raise "#{uri} has no content"
150
+ ['html', body.children.to_html]
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,244 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <xs:schema targetNamespace="http://www.w3.org/2005/Atom" elementFormDefault="qualified"
3
+ attributeFormDefault="unqualified"
4
+ xmlns:atom="http://www.w3.org/2005/Atom"
5
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
6
+ xmlns:xml="http://www.w3.org/XML/1998/namespace">
7
+ <xs:annotation>
8
+ <xs:documentation>
9
+ This version of the Atom schema is based on version 1.0 of the format specifications,
10
+ found here http://www.atomenabled.org/developers/syndication/atom-format-spec.php.
11
+ </xs:documentation>
12
+ </xs:annotation>
13
+ <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/03/xml.xsd" />
14
+ <xs:annotation>
15
+ <xs:documentation>
16
+ An Atom document may have two root elements, feed and entry, as defined in section 2.
17
+ </xs:documentation>
18
+ </xs:annotation>
19
+ <xs:element name="feed" type="atom:feedType"/>
20
+ <xs:element name="entry" type="atom:entryType"/>
21
+ <xs:complexType name="textType" mixed="true">
22
+ <xs:annotation>
23
+ <xs:documentation>
24
+ The Atom text construct is defined in section 3.1 of the format spec.
25
+ </xs:documentation>
26
+ </xs:annotation>
27
+ <xs:sequence>
28
+ <xs:any namespace="http://www.w3.org/1999/xhtml" minOccurs="0"/>
29
+ </xs:sequence>
30
+ <xs:attribute name="type" >
31
+ <xs:simpleType>
32
+ <xs:restriction base="xs:token">
33
+ <xs:enumeration value="text"/>
34
+ <xs:enumeration value="html"/>
35
+ <xs:enumeration value="xhtml"/>
36
+ </xs:restriction>
37
+ </xs:simpleType>
38
+ </xs:attribute>
39
+ <xs:attributeGroup ref="atom:commonAttributes"/>
40
+ </xs:complexType>
41
+ <xs:complexType name="personType">
42
+ <xs:annotation>
43
+ <xs:documentation>
44
+ The Atom person construct is defined in section 3.2 of the format spec.
45
+ </xs:documentation>
46
+ </xs:annotation>
47
+ <xs:choice minOccurs="1" maxOccurs="unbounded">
48
+ <xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
49
+ <xs:element name="uri" type="atom:uriType" minOccurs="0" maxOccurs="1" />
50
+ <xs:element name="email" type="atom:emailType" minOccurs="0" maxOccurs="1" />
51
+ <xs:any namespace="##other"/>
52
+ </xs:choice>
53
+ <xs:attributeGroup ref="atom:commonAttributes"/>
54
+ </xs:complexType>
55
+ <xs:simpleType name="emailType">
56
+ <xs:annotation>
57
+ <xs:documentation>
58
+ Schema definition for an email address.
59
+ </xs:documentation>
60
+ </xs:annotation>
61
+ <xs:restriction base="xs:normalizedString">
62
+ <!--
63
+ <xs:pattern value="\w+@(\w+\.)+\w+" />
64
+ -->
65
+ <xs:maxLength value="254"/>
66
+ <xs:pattern value="[_\-a-zA-Z0-9\.\+]+@[a-zA-Z0-9](\.?[\-a-zA-Z0-9]*[a-zA-Z0-9])*"/>
67
+ </xs:restriction>
68
+ </xs:simpleType>
69
+ <xs:complexType name="feedType">
70
+ <xs:annotation>
71
+ <xs:documentation>
72
+ The Atom feed construct is defined in section 4.1.1 of the format spec.
73
+ </xs:documentation>
74
+ </xs:annotation>
75
+ <xs:choice minOccurs="3" maxOccurs="unbounded">
76
+ <xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
77
+ <xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded" />
78
+ <xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
79
+ <xs:element name="generator" type="atom:generatorType" minOccurs="0" maxOccurs="1" />
80
+ <xs:element name="icon" type="atom:iconType" minOccurs="0" maxOccurs="1" />
81
+ <xs:element name="id" type="atom:idType" minOccurs="1" maxOccurs="1" />
82
+ <xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded" />
83
+ <xs:element name="logo" type="atom:logoType" minOccurs="0" maxOccurs="1" />
84
+ <xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1" />
85
+ <xs:element name="subtitle" type="atom:textType" minOccurs="0" maxOccurs="1" />
86
+ <xs:element name="title" type="atom:textType" minOccurs="1" maxOccurs="1" />
87
+ <xs:element name="updated" type="atom:dateTimeType" minOccurs="1" maxOccurs="1" />
88
+ <xs:element name="entry" type="atom:entryType" minOccurs="0" maxOccurs="unbounded" />
89
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
90
+ </xs:choice>
91
+ <xs:attributeGroup ref="atom:commonAttributes"/>
92
+ </xs:complexType>
93
+ <xs:complexType name="entryType">
94
+ <xs:annotation>
95
+ <xs:documentation>
96
+ The Atom entry construct is defined in section 4.1.2 of the format spec.
97
+ </xs:documentation>
98
+ </xs:annotation>
99
+ <xs:choice maxOccurs="unbounded">
100
+ <xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
101
+ <xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded" />
102
+ <xs:element name="content" type="atom:contentType" minOccurs="0" maxOccurs="1" />
103
+ <xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded" />
104
+ <xs:element name="id" type="atom:idType" minOccurs="1" maxOccurs="1" />
105
+ <xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded" />
106
+ <xs:element name="published" type="atom:dateTimeType" minOccurs="0" maxOccurs="1" />
107
+ <xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1" />
108
+ <xs:element name="source" type="atom:textType" minOccurs="0" maxOccurs="1" />
109
+ <xs:element name="summary" type="atom:textType" minOccurs="0" maxOccurs="1" />
110
+ <xs:element name="title" type="atom:textType" minOccurs="1" maxOccurs="1" />
111
+ <xs:element name="updated" type="atom:dateTimeType" minOccurs="1" maxOccurs="1" />
112
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
113
+ </xs:choice>
114
+ <xs:attributeGroup ref="atom:commonAttributes"/>
115
+ </xs:complexType>
116
+ <xs:complexType name="contentType" mixed="true">
117
+ <xs:annotation>
118
+ <xs:documentation>
119
+ The Atom content construct is defined in section 4.1.3 of the format spec.
120
+ </xs:documentation>
121
+ </xs:annotation>
122
+ <xs:sequence>
123
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" />
124
+ </xs:sequence>
125
+ <xs:attribute name="type" type="xs:string"/>
126
+ <xs:attribute name="src" type="xs:anyURI"/>
127
+ <xs:attributeGroup ref="atom:commonAttributes"/>
128
+ </xs:complexType>
129
+ <xs:complexType name="categoryType">
130
+ <xs:annotation>
131
+ <xs:documentation>
132
+ The Atom cagegory construct is defined in section 4.2.2 of the format spec.
133
+ </xs:documentation>
134
+ </xs:annotation>
135
+ <xs:attribute name="term" type="xs:string" use="required"/>
136
+ <xs:attribute name="scheme" type="xs:anyURI" use="optional"/>
137
+ <xs:attribute name="label" type="xs:string" use="optional"/>
138
+ <xs:attributeGroup ref="atom:commonAttributes" />
139
+ </xs:complexType>
140
+ <xs:complexType name="generatorType">
141
+ <xs:annotation>
142
+ <xs:documentation>
143
+ The Atom generator element is defined in section 4.2.4 of the format spec.
144
+ </xs:documentation>
145
+ </xs:annotation>
146
+ <xs:simpleContent>
147
+ <xs:extension base="xs:string">
148
+ <xs:attribute name="uri" use="optional" type="xs:anyURI" />
149
+ <xs:attribute name="version" use="optional" type="xs:string" />
150
+ <xs:attributeGroup ref="atom:commonAttributes"/>
151
+ </xs:extension>
152
+ </xs:simpleContent>
153
+ </xs:complexType>
154
+ <xs:complexType name="iconType">
155
+ <xs:annotation>
156
+ <xs:documentation>
157
+ The Atom icon construct is defined in section 4.2.5 of the format spec.
158
+ </xs:documentation>
159
+ </xs:annotation>
160
+ <xs:simpleContent>
161
+ <xs:extension base="xs:anyURI">
162
+ <xs:attributeGroup ref="atom:commonAttributes"/>
163
+ </xs:extension>
164
+ </xs:simpleContent>
165
+ </xs:complexType>
166
+ <xs:complexType name="idType">
167
+ <xs:annotation>
168
+ <xs:documentation>
169
+ The Atom id construct is defined in section 4.2.6 of the format spec.
170
+ </xs:documentation>
171
+ </xs:annotation>
172
+ <xs:simpleContent>
173
+ <xs:extension base="xs:anyURI">
174
+ <xs:attributeGroup ref="atom:commonAttributes"/>
175
+ </xs:extension>
176
+ </xs:simpleContent>
177
+ </xs:complexType>
178
+ <xs:complexType name="linkType" mixed="true">
179
+ <xs:annotation>
180
+ <xs:documentation>
181
+ The Atom link construct is defined in section 3.4 of the format spec.
182
+ </xs:documentation>
183
+ </xs:annotation>
184
+ <xs:attribute name="href" use="required" type="xs:anyURI" />
185
+ <xs:attribute name="rel" type="xs:string" use="optional"/>
186
+ <xs:attribute name="type" use="optional" type="xs:string" />
187
+ <xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN" />
188
+ <xs:attribute name="title" use="optional" type="xs:string" />
189
+ <xs:attribute name="length" use="optional" type="xs:positiveInteger" />
190
+ <xs:attributeGroup ref="atom:commonAttributes"/>
191
+ </xs:complexType>
192
+ <xs:complexType name="logoType">
193
+ <xs:annotation>
194
+ <xs:documentation>
195
+ The Atom logo construct is defined in section 4.2.8 of the format spec.
196
+ </xs:documentation>
197
+ </xs:annotation>
198
+ <xs:simpleContent>
199
+ <xs:extension base="xs:anyURI">
200
+ <xs:attributeGroup ref="atom:commonAttributes"/>
201
+ </xs:extension>
202
+ </xs:simpleContent>
203
+ </xs:complexType>
204
+ <xs:complexType name="sourceType">
205
+ <xs:annotation>
206
+ <xs:documentation>
207
+ The Atom source construct is defined in section 4.2.11 of the format spec.
208
+ </xs:documentation>
209
+ </xs:annotation>
210
+ <xs:choice maxOccurs="unbounded">
211
+ <xs:element name="author" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
212
+ <xs:element name="category" type="atom:categoryType" minOccurs="0" maxOccurs="unbounded"/>
213
+ <xs:element name="contributor" type="atom:personType" minOccurs="0" maxOccurs="unbounded"/>
214
+ <xs:element name="generator" type="atom:generatorType" minOccurs="0" maxOccurs="1"/>
215
+ <xs:element name="icon" type="atom:iconType" minOccurs="0" maxOccurs="1"/>
216
+ <xs:element name="id" type="atom:idType" minOccurs="0" maxOccurs="1"/>
217
+ <xs:element name="link" type="atom:linkType" minOccurs="0" maxOccurs="unbounded"/>
218
+ <xs:element name="logo" type="atom:logoType" minOccurs="0" maxOccurs="1"/>
219
+ <xs:element name="rights" type="atom:textType" minOccurs="0" maxOccurs="1"/>
220
+ <xs:element name="subtitle" type="atom:textType" minOccurs="0" maxOccurs="1"/>
221
+ <xs:element name="title" type="atom:textType" minOccurs="0" maxOccurs="1"/>
222
+ <xs:element name="updated" type="atom:dateTimeType" minOccurs="0" maxOccurs="1"/>
223
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
224
+ </xs:choice>
225
+ <xs:attributeGroup ref="atom:commonAttributes"/>
226
+ </xs:complexType>
227
+ <xs:complexType name="uriType">
228
+ <xs:simpleContent>
229
+ <xs:extension base="xs:anyURI">
230
+ <xs:attributeGroup ref="atom:commonAttributes"/>
231
+ </xs:extension>
232
+ </xs:simpleContent>
233
+ </xs:complexType>
234
+ <xs:complexType name="dateTimeType">
235
+ <xs:simpleContent>
236
+ <xs:extension base="xs:dateTime">
237
+ <xs:attributeGroup ref="atom:commonAttributes"/>
238
+ </xs:extension>
239
+ </xs:simpleContent>
240
+ </xs:complexType>
241
+ <xs:attributeGroup name="commonAttributes">
242
+ <xs:anyAttribute namespace="##other"/>
243
+ </xs:attributeGroup>
244
+ </xs:schema>