mill 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>