hermeneutics 1.8
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.
- checksums.yaml +7 -0
- data/LICENSE +29 -0
- data/bin/hermesmail +262 -0
- data/etc/exim.conf +34 -0
- data/lib/hermeneutics/addrs.rb +687 -0
- data/lib/hermeneutics/boxes.rb +321 -0
- data/lib/hermeneutics/cgi.rb +253 -0
- data/lib/hermeneutics/cli/pop.rb +102 -0
- data/lib/hermeneutics/color.rb +275 -0
- data/lib/hermeneutics/contents.rb +351 -0
- data/lib/hermeneutics/css.rb +261 -0
- data/lib/hermeneutics/escape.rb +826 -0
- data/lib/hermeneutics/html.rb +462 -0
- data/lib/hermeneutics/mail.rb +105 -0
- data/lib/hermeneutics/message.rb +626 -0
- data/lib/hermeneutics/tags.rb +317 -0
- data/lib/hermeneutics/transports.rb +230 -0
- data/lib/hermeneutics/types.rb +137 -0
- data/lib/hermeneutics/version.rb +32 -0
- metadata +83 -0
@@ -0,0 +1,462 @@
|
|
1
|
+
#
|
2
|
+
# hermeneutics/html.rb -- smart HTML generation
|
3
|
+
#
|
4
|
+
|
5
|
+
require "hermeneutics/escape"
|
6
|
+
require "hermeneutics/contents"
|
7
|
+
|
8
|
+
|
9
|
+
module Hermeneutics
|
10
|
+
|
11
|
+
# = Example
|
12
|
+
#
|
13
|
+
# require "hermeneutics/color"
|
14
|
+
# require "hermeneutics/html"
|
15
|
+
#
|
16
|
+
# class MyHtml < Hermeneutics::Html
|
17
|
+
# def build
|
18
|
+
# html {
|
19
|
+
# head {
|
20
|
+
# title { "Example" }
|
21
|
+
# comment "created as an example, #{Time.now}"
|
22
|
+
# }
|
23
|
+
# body( :bgcolor => Hermeneutics::Color.from_s( "ffffef")) {
|
24
|
+
# h1 {
|
25
|
+
# pcdata "Ruby "
|
26
|
+
# a( :href => "www.w3.org") { "Html" }
|
27
|
+
# _ { " example" }
|
28
|
+
# }
|
29
|
+
# p { "Some text.\nBye." }
|
30
|
+
# p {
|
31
|
+
# self << "link"
|
32
|
+
# br
|
33
|
+
# a( :href => "www.w3.org") { "Html" }
|
34
|
+
# }
|
35
|
+
# }
|
36
|
+
# }
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Hermeneutics::Html.document
|
41
|
+
#
|
42
|
+
class Html
|
43
|
+
|
44
|
+
class <<self
|
45
|
+
attr_accessor :main
|
46
|
+
def inherited cls
|
47
|
+
Html.main = cls
|
48
|
+
end
|
49
|
+
def open out = nil
|
50
|
+
i = (@main||self).new
|
51
|
+
i.generate out do
|
52
|
+
yield i
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def document *args, &block
|
56
|
+
open do |i|
|
57
|
+
i.document *args, &block
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def write_file name = nil
|
61
|
+
name ||= (File.basename $0, ".rb") + ".html"
|
62
|
+
File.open name, "w" do |f|
|
63
|
+
open f do |i|
|
64
|
+
if block_given? then
|
65
|
+
yield i
|
66
|
+
else
|
67
|
+
i.document
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def generate out = nil
|
75
|
+
g = @generator
|
76
|
+
begin
|
77
|
+
@generator = Generator.new out||$stdout
|
78
|
+
yield
|
79
|
+
ensure
|
80
|
+
@generator = g
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def document *args, &block
|
85
|
+
doctype_header
|
86
|
+
build *args, &block
|
87
|
+
end
|
88
|
+
|
89
|
+
def doctype_header
|
90
|
+
@generator.doctype "html"
|
91
|
+
end
|
92
|
+
|
93
|
+
def build
|
94
|
+
html { body { h1 { "It works." } } }
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def language
|
99
|
+
if ENV[ "LANG"] =~ /\A\w{2,}/ then
|
100
|
+
r = $&
|
101
|
+
r.gsub! /_/, "-"
|
102
|
+
r
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
class Generator
|
108
|
+
attr_accessor :close_standalone, :assign_attributes, :cdata_block
|
109
|
+
def initialize out
|
110
|
+
@out = out
|
111
|
+
@ent = Entities.new
|
112
|
+
@nl, @ind = true, [ ""]
|
113
|
+
end
|
114
|
+
def encoding
|
115
|
+
case @out
|
116
|
+
when IO then @out.external_encoding||Encoding.default_external
|
117
|
+
else @out.encoding
|
118
|
+
end
|
119
|
+
end
|
120
|
+
def file_path
|
121
|
+
@out.path
|
122
|
+
rescue NoMethodError
|
123
|
+
end
|
124
|
+
def plain str
|
125
|
+
do_ind
|
126
|
+
@out << (@ent.encode str)
|
127
|
+
end
|
128
|
+
# nls
|
129
|
+
# 0 = no newline
|
130
|
+
# 1 = newline after
|
131
|
+
# 2 = newline after both
|
132
|
+
# 3 = and advance indent
|
133
|
+
# 4 = Block without any indent
|
134
|
+
def tag tag, type, attrs = nil
|
135
|
+
tag = tag.to_s
|
136
|
+
nls = type & 0xf
|
137
|
+
if (type & 0x10).nonzero? then
|
138
|
+
brace nls>0 do
|
139
|
+
@out << tag
|
140
|
+
mkattrs attrs
|
141
|
+
@out << " /" if @close_standalone
|
142
|
+
end
|
143
|
+
else
|
144
|
+
begin
|
145
|
+
brk if nls>1
|
146
|
+
brace nls>1 do
|
147
|
+
@out << tag
|
148
|
+
mkattrs attrs
|
149
|
+
end
|
150
|
+
if nls >3 then
|
151
|
+
verbose_block yield
|
152
|
+
else
|
153
|
+
indent_if nls>2 do
|
154
|
+
if block_given? then
|
155
|
+
r = yield
|
156
|
+
plain r if String === r
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
ensure
|
161
|
+
brk if nls>1
|
162
|
+
brace nls>0 do
|
163
|
+
@out << "/" << tag
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
# Processing Instruction
|
170
|
+
def pi_tag tag, attrs = nil
|
171
|
+
tag = tag.to_s
|
172
|
+
brace true do
|
173
|
+
begin
|
174
|
+
@out << "?" << tag
|
175
|
+
mkattrs attrs
|
176
|
+
ensure
|
177
|
+
@out << " ?"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
def doctype *args
|
182
|
+
brace true do
|
183
|
+
@out << "!DOCTYPE"
|
184
|
+
args.each { |x|
|
185
|
+
@out << " "
|
186
|
+
if x =~ /\W/ then
|
187
|
+
@out << '"' << (@ent.encode x) << '"'
|
188
|
+
else
|
189
|
+
@out << x
|
190
|
+
end
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
def comment str
|
195
|
+
if str =~ /\A.*\z/ then
|
196
|
+
brace_comment do
|
197
|
+
@out << " " << str << " "
|
198
|
+
end
|
199
|
+
else
|
200
|
+
brace_comment do
|
201
|
+
brk
|
202
|
+
out_brk str
|
203
|
+
do_ind
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
def verbose_block str
|
208
|
+
if @cdata_block then
|
209
|
+
@out << "/* "
|
210
|
+
brace false do
|
211
|
+
@out << "![CDATA["
|
212
|
+
@out << " */" << $/
|
213
|
+
@out << str
|
214
|
+
@out << $/ << "/* "
|
215
|
+
@out << "]]"
|
216
|
+
end
|
217
|
+
@out << " */"
|
218
|
+
else
|
219
|
+
out_brk str
|
220
|
+
end
|
221
|
+
end
|
222
|
+
private
|
223
|
+
def brk
|
224
|
+
unless @nl then
|
225
|
+
@nl = true
|
226
|
+
@out << $/
|
227
|
+
end
|
228
|
+
end
|
229
|
+
def out_brk str
|
230
|
+
@out << str
|
231
|
+
@nl = str !~ /.\z/
|
232
|
+
brk
|
233
|
+
end
|
234
|
+
def do_ind
|
235
|
+
if @nl then
|
236
|
+
@out << @ind.last
|
237
|
+
@nl = false
|
238
|
+
end
|
239
|
+
end
|
240
|
+
def brace nl
|
241
|
+
do_ind
|
242
|
+
@out << "<"
|
243
|
+
yield
|
244
|
+
nil
|
245
|
+
ensure
|
246
|
+
@out << ">"
|
247
|
+
brk if nl
|
248
|
+
end
|
249
|
+
def brace_comment
|
250
|
+
brace true do
|
251
|
+
@out << "!--"
|
252
|
+
yield
|
253
|
+
@out << "--"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
def indent_if flag
|
257
|
+
if flag then
|
258
|
+
indent do yield end
|
259
|
+
else
|
260
|
+
yield
|
261
|
+
end
|
262
|
+
end
|
263
|
+
INDENT = 2
|
264
|
+
def indent
|
265
|
+
@ind.push @ind.last + " "*INDENT
|
266
|
+
yield
|
267
|
+
ensure
|
268
|
+
@ind.pop
|
269
|
+
end
|
270
|
+
def mkattrs attrs
|
271
|
+
attrs or return
|
272
|
+
attrs.each { |k,v|
|
273
|
+
if Symbol === k then k = k.new_string ; k.gsub! /_/, "-" end
|
274
|
+
v = case v
|
275
|
+
when Array then v.compact.join " "
|
276
|
+
when true then k.to_s if @assign_attributes
|
277
|
+
when nil, false then next
|
278
|
+
else v.to_s
|
279
|
+
end
|
280
|
+
@out << " " << k
|
281
|
+
@out << "=\"" << (@ent.encode v) << "\"" if v.notempty?
|
282
|
+
}
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def file_path ; @generator.file_path ; end
|
287
|
+
|
288
|
+
NBSP = Entities::NAMES[ "nbsp"]
|
289
|
+
|
290
|
+
TAGS = {
|
291
|
+
a:0, abbr:0, address:1, article:3, aside:3, audio:3, b:0, bdi:0, bdo:2,
|
292
|
+
blockquote:3, body:2, button:3, canvas:1, caption:1, cite:0, code:0,
|
293
|
+
colgroup:3, data:0, datalist:3, dd:1, del:0, details:3, dfn:0, dialog:0,
|
294
|
+
div:3, dl:3, dt:1, em:0, fieldset:3, figcaption:1, figure:3, footer:3,
|
295
|
+
form:3, h1:1, h2:1, h3:1, h4:1, h5:1, h6:1, head:3, header:3, html:2,
|
296
|
+
i:0, iframe:3, ins:0, kbd:0, label:0, legend:1, li:1, main:3, map:3,
|
297
|
+
mark:0, meter:0, nav:3, noscript:3, object:3, ol:3, optgroup:3,
|
298
|
+
option:1, output:0, p:2, picture:3, pre:1, progress:0, q:0, rp:0, rt:0,
|
299
|
+
ruby:2, s:0, samp:0, section:3, select:3, small:0, span:0, strong:0,
|
300
|
+
sub:0, summary:1, sup:0, svg:3, table:3, tbody:3, td:1, template:3,
|
301
|
+
textarea:1, tfoot:3, th:1, thead:3, time:0, title:1, tr:3, u:0, ul:3,
|
302
|
+
var:0, video:3,
|
303
|
+
|
304
|
+
# tags containing foreign code blocks
|
305
|
+
script:4, style:4,
|
306
|
+
|
307
|
+
# void tags
|
308
|
+
area:0x11, base:0x11, br:0x11, col:0x11, embed:0x11, hr:0x11, img:0x10,
|
309
|
+
input:0x10, keygen:0x11, link:0x11, meta:0x11, param:0x11, source:0x11,
|
310
|
+
track:0x11, wbr:0x10,
|
311
|
+
}
|
312
|
+
|
313
|
+
# remove Kernel methods of the same name: :p, :select, :sub
|
314
|
+
(TAGS.keys & (private_instance_methods +
|
315
|
+
protected_instance_methods +
|
316
|
+
instance_methods)).each { |m| undef_method m }
|
317
|
+
|
318
|
+
def tag? name
|
319
|
+
TAGS[ name]
|
320
|
+
end
|
321
|
+
|
322
|
+
def method_missing name, *args, &block
|
323
|
+
t = tag? name
|
324
|
+
t or super
|
325
|
+
if String === args.last then
|
326
|
+
b = args.pop
|
327
|
+
@generator.tag name, t, *args do b end
|
328
|
+
else
|
329
|
+
@generator.tag name, t, *args, &block
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def pcdata *strs
|
334
|
+
strs.each { |s|
|
335
|
+
s or next
|
336
|
+
@generator.plain s
|
337
|
+
}
|
338
|
+
nil
|
339
|
+
end
|
340
|
+
|
341
|
+
def << str
|
342
|
+
@generator.plain str if str
|
343
|
+
self
|
344
|
+
end
|
345
|
+
|
346
|
+
def _ str = nil
|
347
|
+
@generator.plain str||yield
|
348
|
+
nil
|
349
|
+
end
|
350
|
+
|
351
|
+
def comment str
|
352
|
+
@generator.comment str
|
353
|
+
end
|
354
|
+
|
355
|
+
def javascript str = nil, &block
|
356
|
+
mime = { type: "text/javascript" }
|
357
|
+
script mime, str, &block
|
358
|
+
end
|
359
|
+
|
360
|
+
def html **attrs
|
361
|
+
attrs[ :"lang"] ||= language
|
362
|
+
method_missing :html, **attrs do yield end
|
363
|
+
end
|
364
|
+
|
365
|
+
def head attrs = nil
|
366
|
+
method_missing :head, attrs do
|
367
|
+
meta charset: @generator.encoding
|
368
|
+
yield
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def form attrs, &block
|
373
|
+
attrs[ :method] ||= if attrs[ :enctype] == "multipart/form-data" then
|
374
|
+
"post"
|
375
|
+
else
|
376
|
+
"get"
|
377
|
+
end
|
378
|
+
@tabindex = 0
|
379
|
+
method_missing :form, attrs, &block
|
380
|
+
ensure
|
381
|
+
@tabindex = nil
|
382
|
+
end
|
383
|
+
|
384
|
+
Field = Struct[ :type, :attrs]
|
385
|
+
|
386
|
+
def field type, attrs
|
387
|
+
attrs[ :id] ||= attrs[ :name]
|
388
|
+
@tabindex += 1
|
389
|
+
attrs[ :tabindex] ||= @tabindex
|
390
|
+
Field[ type, attrs]
|
391
|
+
end
|
392
|
+
|
393
|
+
def label field = nil, attrs = nil, &block
|
394
|
+
if String === attrs then
|
395
|
+
label field do attrs end
|
396
|
+
else
|
397
|
+
if Field === field or attrs then
|
398
|
+
attrs = attrs.merge for: field.attrs[ :id]
|
399
|
+
else
|
400
|
+
attrs = field
|
401
|
+
end
|
402
|
+
method_missing :label, attrs, &block
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def input arg, &block
|
407
|
+
if Field === arg then
|
408
|
+
case arg.type
|
409
|
+
when /select/i then
|
410
|
+
method_missing :select, arg.attrs, &block
|
411
|
+
when /textarea/i then
|
412
|
+
block and
|
413
|
+
raise ArgumentError, "Field textarea: use the value attribute."
|
414
|
+
v = arg.attrs.delete :value
|
415
|
+
method_missing :textarea, arg.attrs do v end
|
416
|
+
else
|
417
|
+
arg.attrs[ :type] ||= arg.type
|
418
|
+
method_missing :input, arg.attrs, &block
|
419
|
+
end
|
420
|
+
else
|
421
|
+
method_missing :input, arg, &block
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
class XHtml < Html
|
428
|
+
|
429
|
+
def html **attrs
|
430
|
+
attrs[ :xmlns] ||= "http://www.w3.org/1999/xhtml"
|
431
|
+
attrs[ :"xml:lang"] = language
|
432
|
+
attrs[ :lang] = ""
|
433
|
+
super
|
434
|
+
end
|
435
|
+
|
436
|
+
def a attrs = nil
|
437
|
+
attrs[ :name] ||= attrs[ :id] if attrs
|
438
|
+
super
|
439
|
+
end
|
440
|
+
|
441
|
+
private
|
442
|
+
|
443
|
+
def generate out
|
444
|
+
super do
|
445
|
+
@generator.close_standalone = true
|
446
|
+
@generator.assign_attributes = true
|
447
|
+
@generator.cdata_block = true
|
448
|
+
yield
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def doctype_header
|
453
|
+
prop = { version: "1.0", encoding: @generator.encoding }
|
454
|
+
@generator.pi_tag :xml, prop
|
455
|
+
@generator.doctype "html", "PUBLIC", "-//W3C//DTD XHTML 1.1//EN",
|
456
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
|