infod 0.0.2 → 0.0.3

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 (83) hide show
  1. data/infod.rb +52 -12
  2. data/infod/{Th/404.rb → 404.rb} +4 -16
  3. data/infod/500.rb +53 -0
  4. data/infod/GET.rb +104 -0
  5. data/infod/HEAD.rb +23 -0
  6. data/infod/HTTP.rb +105 -0
  7. data/infod/{Th/PATCH.rb → PATCH.rb} +0 -0
  8. data/infod/POST.rb +34 -0
  9. data/infod/audio.rb +30 -0
  10. data/infod/blog.rb +34 -0
  11. data/infod/cal.rb +72 -0
  12. data/infod/{Es/code.rb → code.rb} +7 -4
  13. data/infod/constants.rb +55 -0
  14. data/infod/{Es/css.rb → css.rb} +0 -0
  15. data/infod/{Es/csv.rb → csv.rb} +0 -0
  16. data/infod/{Es/du.rb → du.rb} +0 -0
  17. data/infod/edit.rb +73 -0
  18. data/infod/{H/facets.rb → facets.rb} +20 -11
  19. data/infod/{Es/feed.rb → feed.rb} +17 -16
  20. data/infod/{Es/find.rb → find.rb} +2 -3
  21. data/infod/forum.rb +13 -0
  22. data/infod/{Es/fs.rb → fs.rb} +5 -2
  23. data/infod/glob.rb +26 -0
  24. data/infod/graph.rb +123 -0
  25. data/infod/{Es/grep.rb → grep.rb} +2 -2
  26. data/infod/{Es/groonga.rb → groonga.rb} +41 -33
  27. data/infod/{H/histogram.rb → histogram.rb} +23 -16
  28. data/infod/html.rb +231 -0
  29. data/infod/{Es/image.rb → image.rb} +16 -26
  30. data/infod/{Es/index.rb → index.rb} +38 -25
  31. data/infod/infod.rb +52 -12
  32. data/infod/json.rb +38 -0
  33. data/infod/{Es/kv.rb → kv.rb} +3 -9
  34. data/infod/{Y.rb → lambda.rb} +18 -1
  35. data/infod/ls.rb +49 -0
  36. data/infod/mail.rb +90 -0
  37. data/infod/{Es/man.rb → man.rb} +3 -15
  38. data/infod/{H/microblog.rb → microblog.rb} +22 -31
  39. data/infod/{K.rb → mime.rb} +68 -52
  40. data/infod/{N.rb → names.rb} +78 -45
  41. data/infod/page.rb +13 -0
  42. data/infod/postscript.rb +26 -0
  43. data/infod/rdf.rb +51 -0
  44. data/infod/{Rb.rb → ruby.rb} +18 -33
  45. data/infod/{Es/schema.rb → schema.rb} +22 -7
  46. data/infod/{Es/search.rb → search.rb} +5 -11
  47. data/infod/{Es/sh.rb → sh.rb} +0 -0
  48. data/infod/{Es/text.rb → text.rb} +33 -29
  49. data/infod/{H/threads.rb → threads.rb} +17 -27
  50. data/infod/{H/time.rb → time.rb} +14 -34
  51. data/infod/{H/who.rb → whois.rb} +6 -4
  52. data/infod/{H/wiki.rb → wiki.rb} +0 -0
  53. metadata +54 -64
  54. data/config.ru +0 -3
  55. data/infod/Es.rb +0 -31
  56. data/infod/Es/filter.rb +0 -75
  57. data/infod/Es/glob.rb +0 -22
  58. data/infod/Es/html.rb +0 -271
  59. data/infod/Es/in.rb +0 -68
  60. data/infod/Es/json.rb +0 -68
  61. data/infod/Es/ls.rb +0 -58
  62. data/infod/Es/mail.rb +0 -87
  63. data/infod/Es/mime.rb +0 -59
  64. data/infod/Es/out.rb +0 -52
  65. data/infod/Es/pager.rb +0 -34
  66. data/infod/Es/pdf.rb +0 -19
  67. data/infod/Es/rdf.rb +0 -35
  68. data/infod/H.rb +0 -15
  69. data/infod/H/audio.rb +0 -19
  70. data/infod/H/blog.rb +0 -15
  71. data/infod/H/cal.rb +0 -81
  72. data/infod/H/edit.rb +0 -88
  73. data/infod/H/forum.rb +0 -4
  74. data/infod/H/hf.rb +0 -114
  75. data/infod/H/mail.rb +0 -92
  76. data/infod/Th.rb +0 -36
  77. data/infod/Th/500.rb +0 -41
  78. data/infod/Th/GET.rb +0 -62
  79. data/infod/Th/HEAD.rb +0 -5
  80. data/infod/Th/POST.rb +0 -39
  81. data/infod/Th/perf.rb +0 -37
  82. data/infod/Th/uid.rb +0 -24
  83. data/infod/Th/util.rb +0 -89
data/infod/mail.rb ADDED
@@ -0,0 +1,90 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ begin
5
+ require 'tmail'
6
+ rescue LoadError => e
7
+ end
8
+
9
+ def triplrTmail &f
10
+ messagePath = ->id{
11
+ h = id.h # hash
12
+ '/msg/' + h[0..1] + '/' + h[2] + '/' + id}
13
+
14
+ (TMail::Mail.load node).do{|m| # load
15
+ d = m.message_id; return unless d # parse successful?
16
+ id = d[1..-2] # message-ID
17
+ e = messagePath[id] # webized ID
18
+ yield e, DC+'identifier', id # original ID
19
+ yield e, DC+'source', self # original file
20
+ yield e, Type, E[SIOCt + 'MailMessage']
21
+ yield e, Type, E[SIOC + 'Post']
22
+ yield e, Date, m.date.iso8601 if m.date
23
+ yield e, Title, m.subject.to_utf8
24
+ yield e, SIOC+'name', m.friendly_from.to_utf8
25
+ yield e, Creator, E['/m/'+m.from[0].to_utf8]
26
+ m.header['x-original-to'].do{|f|
27
+ yield e, SIOC+'reply_to', E[URI.escape "mailto:#{f}?References=<#{e}>&In-Reply-To=<#{e}>&Subject=#{m.subject.to_utf8}"] }
28
+ %w{to cc bcc}.map{|to|
29
+ m.send(to).do{|to| to.map{|to|
30
+ yield e, To, E['/m/'+to.to_utf8]}}}
31
+ %w{in_reply_to references}.map{|ref|
32
+ m.send(ref).do{|refs| refs.map{|r|
33
+ yield e, SIOC+'reply_of', E[messagePath[r[1..-2]]]}}}
34
+ # minimal local markup to use as HTML-literal even if decoupled from specialized view
35
+ yield e, Content, H([{_: :pre, class: :mail, style: 'white-space: pre-wrap',
36
+ c: m.concat_message(e.E,0,&f).gsub(/^\s*(&gt;)(&gt;|\s)*\n/,"").lines.to_a.map{|l| # < skip quoted emptylines v tag quoted lines
37
+ {_: :span, class: ((l.match /(^\s*(&gt;|On[^\n]+(said|wrote))[^\n]*)\n/) ? 'q' : 'u'), c: [ l.chomp, "\n" ]}}},
38
+ {_: :style, c: "pre.mail .q {background-color:#0018ff;color:#fff}\npre.mail a {background-color: #91acb3;color:#fff}\npre.mail img {max-width:100%}"}])}
39
+ rescue Exception => e
40
+ puts e
41
+ end
42
+
43
+ def triplrMailMessage &f
44
+ insertDocs :triplrTmail, nil, [To,SIOC+'reply_of'], &f
45
+ end
46
+ =begin
47
+ there's another mail library called Mail, as of v2.5.4 takes 50x as long as tmail (apt-get install ruby-tmail)
48
+ HEAD 200 http://m/m/2013/12/01/?nocache=&triplr=triplrMail curl/7.33.0 5.4003388
49
+ HEAD 200 http://m/m/2013/12/01/?nocache=&triplr=triplrTmail curl/7.33.0 0.1198720
50
+
51
+ almost a copy of above works but identifiers are not wrapped in <> - with caching it might be fast enough..
52
+
53
+ =end
54
+
55
+ end
56
+
57
+ module TMail
58
+ class Mail
59
+ def unicode_body
60
+ unquoted_body.to_utf8
61
+ end
62
+ def concat_message i, partCount=0, &f
63
+ if multipart?
64
+ parts.map{|part|
65
+ if part.multipart? # and even more nested parts..
66
+ part.concat_message i, partCount, &f
67
+ elsif !attachment?(part) && part.sub_type != 'html'
68
+ part.unicode_body.hrefs true
69
+ else # attachment
70
+ a = i.a('.attache').mk # create container
71
+ p = a.as(part['content-type']['name'] || (partCount.to_s + '.' + (E::MIME.invert[part.content_type] || '.bin').to_s))
72
+ p.w part.body if !p.e # write attachment into message container
73
+ partCount += 1 # display images
74
+ yield i.uri, E::SIOC+'attachment', p
75
+ '<a href="'+p.uri+'">'+(part.main_type=='image' ? '<img src="'+p.uri+'">' : '')+p.uri.label+"</a><br>\n"
76
+ end
77
+ }.join
78
+ else # just a part
79
+ unicode_body.do{|b|
80
+ if content_type && content_type.match(/html/)
81
+ (b.split /<body[^>]*>/)[-1].split(/<\/body>/)[0]
82
+ else
83
+ b.hrefs true
84
+ end}
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ class String; def is_binary_data?; true; end; end
@@ -54,18 +54,6 @@ class E
54
54
  preconv = %w{hu pt tr}.member?(superLang) ? "" : "-k"
55
55
  pageCmd = "zcat #{man} | groff #{preconv} -T html -man -P -D -P #{imagePath}"
56
56
  page = `#{pageCmd}`#.to_utf8
57
-
58
- [[:name,name],
59
- [:acceptLang,acceptLang],
60
- [:lang, lang],
61
- [:langSH, langSH],
62
- [:superLang, superLang],
63
- [:roff,man],
64
- [:htmlBase,htmlBase.d],
65
- [:imagePath,imagePath],
66
- [:localizations,localesAvail],
67
- [:pageCmd,pageCmd]].map{|p| puts [" "*(13-p[0].size),*p].join ' ' }
68
-
69
57
  page = Nokogiri::HTML.parse page
70
58
  body = page.css('body')[0]
71
59
 
@@ -88,9 +76,9 @@ class E
88
76
  # HTMLize hyperlinks
89
77
  # markup commands
90
78
  body.xpath('//text()').map{|a|
91
- a.replace a.to_s.gsub('&gt;','>').hrefs.gsub /\b([^<>\s(]+)\(/mi, '<b>\1</b>('
92
- }
93
-
79
+ a.replace a.to_s.gsub('&gt;','>').hrefs.gsub /\b([^<>\s(]+)\(/mi, '<b>\1</b>('}
80
+ body.css('font').map{|f|f.remove_attribute 'color'}
81
+
94
82
  qs = r['QUERY_STRING'].do{|q| q.empty? ? '' : '?' + q}
95
83
  # href-ize commands
96
84
  body.css('b').map{|b|
@@ -19,14 +19,14 @@ class E
19
19
  yield s, Content, m[3].hrefs(true)
20
20
  yield s, Type, E[SIOCt+'InstantMessage']
21
21
  yield s, Type, E[SIOC+'Post']
22
- yield s, 'hasLink', (m[3].match(/http:\//) ? 'true' : 'false')
23
- yield s, 'hasNum', 'true' if m[3].match(/\d/)} rescue (puts "skipped #{l}")
22
+ yield s, SIOC+'link', (m[3].match(/http:\//) ? 'true' : 'false')
23
+ } rescue nil
24
24
  }
25
25
  end
26
26
 
27
- def tw g='m'
27
+ def tw g
28
28
  no.readlines.shuffle.each_slice(22){|s|
29
- E['https://twitter.com/search/realtime?q='+s.map{|u|'from:'+u.chomp}.intersperse('+OR+').join].addJSON :triplrTweets, g}
29
+ E['https://twitter.com/search/realtime?q='+s.map{|u|'from:'+u.chomp}.intersperse('+OR+').join].insertDocs :triplrTweets, g}
30
30
  end
31
31
 
32
32
  def triplrTweets
@@ -47,38 +47,29 @@ class E
47
47
  }
48
48
  end
49
49
 
50
- fn 'head/chat',->d,e{
51
- t = d.map{|_,r|r[Date]}.flatten.compact.map &:to_time
52
- c = d.map{|_,r|r[Content]}.compact.size
53
- [{_: :title, c: "#{c} post#{c!=1 && 's'} #{t.min} -> #{t.max}"},
54
- (Fn 'head.formats',e)]}
55
-
56
50
  fn 'view/chat',->d,e{
57
- Fn'view/chat/base',d,e,->{d.map{|u,r|Fn 'view/chat/item',r,e}}}
51
+ Fn'baseview/chat',d,e,->{d.map{|u,r|Fn 'itemview/chat',r,e}}}
58
52
 
59
- fn 'view/chat/item',->r,e{
60
- line = r.E.frag
61
- r[Type] && r[Type].map(&:uri).include?(SIOCt+'MailMessage') && r[:mail]=true
62
- r[Content] &&
63
- [{_: :a, id: line},
64
- {_: :a, :class => :date, href: r.url, c: r[Date][0].match(/T([0-9:]{5})/)[1]},
53
+ fn 'itemview/chat',->r,e{
54
+ r[Type] && [*r[Type]].map{|t|t.respond_to?(:uri) && t.uri}.include?(SIOCt+'MailMessage') && r[:mail]=true
55
+ r[Content] && r[Date] && r[Date][0] &&
56
+ [r[Date][0].match(/T([0-9:]{5})/).do{|m|m[1]},
65
57
  {_: :span, :class => :nick, c: {_: :a, href: r[Atom+'/link/alternate'].do{|a|a[0].uri}||r.url,
66
- c: [r[Atom+"/link/image"].do{|p| {_: :img, class: :a, src: p[0].uri}},
58
+ c: [r[Atom+"/link/image"].do{|p| {_: :img, src: p[0].uri, style: "#{rand(2).zero? ? 'left' : 'right'}: 0"}},
67
59
  {_: :span, c: r[SIOC+'name']||r[Creator]||'#'}]}},' ',
68
- {_: :span, :class => :tw,
69
- c: [r[Atom+'/link/media'].do{|a|
70
- a.compact.map{|a|{_: :a, href: r.url, c: {_: :img, src: a.uri}}}},
71
- ((r[Title].to_s==r[Content].to_s || r.uri.match(/twitter/)) && '' ||
72
- {_: :a, href: r.url, c: r[Title],:class => r[:mail] ? :titleMail : :title}),
60
+ {_: :span, :class => :tw, # skip redundant title fields
61
+ c: [((r[Title].to_s == r[Content].to_s || r.uri.match(/twitter/)) && '' ||
62
+ {_: :a, :class => :title, href: r.url, c: r[Title]}), # skip quoted mail-lines & abbreviate
73
63
  r[:mail] ? (r[Content].map{|c|c.lines.to_a.grep(/^[^&@_]+$/)[0..21]}) : r[Content],
74
- ]},' ',
75
- {_: :a, class: :line, href: '#'+line, c: '&nbsp;'},
76
- "<br>\n"] if r.uri}
77
-
78
- fn 'view/chat/base',->d,e,c{
79
- [(H.once e,'chat.head',(H.css '/css/tw'),{_: :style, c: "body, span.nick span, a {background-color: #{E.c}}\n"}),
80
- {:class => :ch, c: c.()},
81
- (H.once e,'chat.tail',{id: :b})]}
64
+ ]},"<br>\n"]}
65
+
66
+ F['view/'+SIOCt+'BoardPost']=->d,e{
67
+ d.map{|u,r|
68
+ {class: :BoardPost, style: "background-color:#ff4500;color:#fff;float:left;border-radius:.8em;padding:.4em;max-width:42em;margin:.5em",
69
+ c: F['itemview/chat'][r,e]}}}
70
+
71
+ fn 'baseview/chat',->d,e,c{
72
+ [(H.once e,'chat.head',(H.css '/css/tw'),{_: :style, c: "body {background-color: #{E.c}}\n"}),c.()]}
82
73
 
83
74
  F['view/'+SIOCt+'InstantMessage']=F['view/chat']
84
75
  F['view/'+SIOCt+'MicroblogPost']=F['view/chat']
@@ -1,37 +1,49 @@
1
1
  #watch __FILE__
2
2
  class E
3
3
 
4
- FSbase = `pwd`.chomp ; BaseLen = FSbase.size
5
- Prefix = '/@' # non-HTTP URI path resolution-prefix
6
- S = /\._/ # data path-separator
4
+ # no link-follow
5
+ def mime
6
+ @mime ||=
7
+ (t = ext.downcase.to_sym
7
8
 
8
- W3 = 'http://www.w3.org/'
9
- Purl = 'http://purl.org/'
10
- FOAF = "http://xmlns.com/foaf/0.1/"
11
- SIOC = 'http://rdfs.org/sioc/ns#'
12
- SIOCt = 'http://rdfs.org/sioc/types#'
13
- MIMEtype = 'http://www.iana.org/assignments/media-types/'
14
- DC = Purl + 'dc/terms/'
15
- Date = DC + 'date'
16
- Modified = DC + 'modified'
17
- Title = DC + 'title'
18
- Name = FOAF + 'name'
19
- To = SIOC + 'addressed_to'
20
- Creator = SIOC + 'has_creator'
21
- Content = SIOC + 'content'
22
- Type = W3 + "1999/02/22-rdf-syntax-ns#type"
23
- RDFs = W3 + '2000/01/rdf-schema#'
24
- HTTP = W3 + '2011/http#'
25
- Header = W3 + '2011/http-headers#'
26
- Posix = W3 + 'ns/posix/'
27
- Stat = Posix + 'stat#'
28
- Label = RDFs + 'label'
29
- EXIF = 'http://www.w3.org/2003/12/exif/ns#'
30
- Audio = 'http://www.semanticdesktop.org/ontologies/nid3/#'
31
- Edit = 'http://buzzword.org.uk/rdf/personal-link-types#edit'
32
- Render = 'render/'
33
- Apache = ENV['apache']
34
- Nginx = ENV['nginx']
9
+ if node.symlink?
10
+ "inode/symlink"
11
+ elsif d?
12
+ "inode/directory"
13
+ elsif MIME[t]
14
+ MIME[t]
15
+ elsif Rack::Mime::MIME_TYPES[t='.'+t.to_s]
16
+ Rack::Mime::MIME_TYPES[t]
17
+ elsif base.index('msg.')==0
18
+ "message/rfc822"
19
+ elsif e
20
+ `file --mime-type -b #{sh}`.chomp
21
+ else
22
+ "application/octet-stream"
23
+ end)
24
+ end
25
+
26
+ # recursively-dereferenced links
27
+ def mimeP
28
+ @mime ||=
29
+ (p = realpath
30
+ unless p
31
+ nil
32
+ else
33
+ t = ((File.extname p).tail || '').downcase.to_sym
34
+ if p.directory?
35
+ "inode/directory"
36
+ elsif MIME[t]
37
+ MIME[t]
38
+ elsif Rack::Mime::MIME_TYPES[t='.'+t.to_s]
39
+ Rack::Mime::MIME_TYPES[t]
40
+ elsif (File.basename p).index('msg.')==0
41
+ "message/rfc822"
42
+ else
43
+ `file --mime-type -b #{Shellwords.escape p.to_s}`.chomp
44
+ end
45
+ end )
46
+ end
35
47
 
36
48
  MIME={
37
49
  aif: 'audio/aif',
@@ -40,6 +52,7 @@ class E
40
52
  avi: 'video/avi',
41
53
  e: 'application/json+rdf',
42
54
  coffee: 'text/plain',
55
+ conf: 'text/plain',
43
56
  css: 'text/css',
44
57
  csv: 'text/comma-separated-values',
45
58
  doc: 'application/word',
@@ -70,12 +83,14 @@ class E
70
83
  pdf: 'application/pdf',
71
84
  pl: 'application/perl',
72
85
  png: 'image/png',
86
+ ps: 'application/postscript',
73
87
  py: 'application/python',
74
88
  rb: 'application/ruby',
75
89
  ru: 'application/ruby',
76
90
  rdf: 'application/rdf+xml',
77
91
  rtf: 'text/rtf',
78
92
  ssv: 'text/semicolon-separated-values',
93
+ tex: 'text/x-tex',
79
94
  textile: 'application/textile',
80
95
  tsv: 'text/tab-separated-values',
81
96
  ttl: 'text/turtle',
@@ -87,9 +102,6 @@ class E
87
102
  xlsx: 'application/excel',
88
103
  }
89
104
 
90
- VideoFile = /(avi|flv|mkv|mpg|mp4|wmv)$/i
91
- AudioFile = /(aif|wav|flac|mp3|m4a|aac|ogg)$/i
92
-
93
105
  MIMEsource={
94
106
  'application/atom+xml' => [:triplrFeed],
95
107
  'application/markdown' => [:triplrMarkdown],
@@ -97,18 +109,21 @@ class E
97
109
  'application/rdf+xml' => [:triplrRDF,:rdfxml],
98
110
  'application/json' => [:triplrJSON],
99
111
  'application/pdf' => [:triplrPDF],
112
+ 'application/postscript'=> [:triplrPS],
100
113
  'application/textile' => [:triplrTextile],
101
114
  'application/uri' => [:triplrUriList],
102
115
  'application/word' => [:triplrWord],
103
116
  'audio/mp4' => [:triplrStdOut,'faad -i',Audio],
104
117
  'audio/mpeg' => [:triplrStdOut,'id3info',Audio,/\((.*?)\)$/],
105
118
  'audio' => [:triplrStdOut,'sndfile-info',Audio],
119
+ 'image' => [:triplrImage],
106
120
  'inode/symlink' => [:triplrSymlink],
107
- 'message/rfc822' => [:triplrMail],
121
+ 'message/rfc822' => [:triplrMailMessage],
108
122
  'text/ansi' => [:triplrANSI],
109
123
  'text/comma-separated-values'=>[:triplrCSV,/,/],
110
124
  'text/log' => [:triplrIRC],
111
125
  'text/man' => [:triplrMan],
126
+ 'text/n3' => [:triplrRDF, :n3],
112
127
  'text/nfo' => [:triplrHref,'cp437'],
113
128
  'text/ntriples' => [:triplrRDF, :ntriples],
114
129
  'text/plain' => [:triplrHref],
@@ -116,6 +131,7 @@ class E
116
131
  'text/semicolon-separated-values'=>[:triplrCSV,/;/],
117
132
  'text/tab-separated-values'=>[:triplrCSV,/\t/],
118
133
  'text/turtle' => [:triplrRDF,:turtle],
134
+ 'text/x-tex' => [:triplrTeX],
119
135
  }
120
136
 
121
137
  # prefer a view even if requested file exists
@@ -124,6 +140,7 @@ class E
124
140
  'application/markdown' => true,
125
141
  'application/json+rdf' => true,
126
142
  'application/org' => true,
143
+ 'application/postscript' => true,
127
144
  'application/textile' => true,
128
145
  'application/uri' => true,
129
146
  'application/word' => true,
@@ -134,6 +151,14 @@ class E
134
151
  'text/man'=>true,
135
152
  'text/nfo'=>true,
136
153
  'text/rtf'=>true,
154
+ 'text/x-tex'=>true,
155
+ }
156
+
157
+ # cache triplr output
158
+ MIMEcache={
159
+ 'audio' => true,
160
+ 'image' => true,
161
+ # '' =>
137
162
  }
138
163
 
139
164
  %w{c c++ fortran haskell makefile pascal perl php python ruby}.map{|t|
@@ -141,25 +166,16 @@ class E
141
166
  MIMEcook[m+t] = true
142
167
  }}
143
168
 
144
- Abbrev={
145
- "dc" => DC,
146
- "foaf" => FOAF,
147
- "rdf" => W3+"1999/02/22-rdf-syntax-ns#",
148
- "rdfs" => RDFs,
149
- "sioc" => SIOC,
150
- "stat" => Stat,
151
- }
152
-
153
- # literal to pathname types
154
- Literal={}
155
- [Purl+'dc/elements/1.1/date',
156
- Date,
157
- DC+'created',
158
- Modified,
159
- ].map{|f|Literal[f]=true}
169
+ def render mime, graph, e
170
+ E[Render+ mime].y graph, e
171
+ end
160
172
 
161
- def == u
162
- to_s == u.to_s
173
+ def triplrMIME &b
174
+ mimeP.do{|mime|
175
+ yield uri, E::Type, (E MIMEtype+mimeP)
176
+ (MIMEsource[mimeP]||
177
+ MIMEsource[mimeP.split(/\//)[0]]).do{|s|
178
+ send *s,&b }}
163
179
  end
164
180
 
165
181
  end
@@ -1,25 +1,17 @@
1
1
  %w{base64 cgi shellwords}.each{|r|require(r)}
2
2
 
3
- def E e
4
- E.new e
5
- end
6
-
7
3
  class E
8
4
 
9
- def E.[] u; u.E end
5
+ attr_reader :uri
6
+ alias_method :url, :uri
10
7
 
11
- def E arg=nil
12
- if arg
13
- E.new arg
14
- else
15
- self
16
- end
8
+ def env r=nil
9
+ r ? (@r = r
10
+ self) : @r
17
11
  end
18
-
19
- attr_reader :uri
20
12
 
21
- def initialize uri
22
- @uri = uri.to_s
13
+ def == u
14
+ to_s == u.to_s
23
15
  end
24
16
 
25
17
  def basename
@@ -39,11 +31,10 @@ class E
39
31
  def ttl; @ttl||= docBase.a('.ttl') end
40
32
 
41
33
  def docBase
42
- uri.split(/#/)[0].E.do{|d|
43
- d.dirname.as d.bare }
34
+ !uri.empty? && uri.split(/#/)[0].do{|u|u.E.do{|d| d.dirname.as d.bare }} || E['']
44
35
  end
45
36
 
46
- # same as above, but w/ URI errors
37
+ # and w/ URI-lib
47
38
  def docBaseURI
48
39
  u = URI uri
49
40
  s = u.scheme
@@ -79,22 +70,27 @@ class E
79
70
  end
80
71
  alias_method :dir, :dirname
81
72
 
82
- # local URL from unlocatable identifier (mail MSGID, etc)
83
- def url
84
- path? ? uri : Prefix + (CGI.escape uri)
73
+ # add hostname to URI if missing
74
+ def hostURL e
75
+ host = 'http://'+e['SERVER_NAME']
76
+ if uri.index('/') == 0
77
+ host + uri
78
+ else
79
+ uri
80
+ end
85
81
  end
86
82
 
87
- # local URL even if locatable-identifier
83
+ # locator for local data about global URI
88
84
  def localURL e
89
85
  # path
90
- if uri.index('/') == 0
86
+ if uri.index('/') == 0 # already a local path
91
87
  uri
92
88
  # host match
93
- elsif uri.index('http://'+e['SERVER_NAME']+'/') == 0
89
+ elsif e && uri.index('http://'+e['SERVER_NAME']+'/') == 0
94
90
  pathSegment.uri
95
91
  # non-local
96
92
  else
97
- Prefix + (CGI.escape uri)
93
+ URIURL + (CGI.escape uri)
98
94
  end
99
95
  end
100
96
 
@@ -116,6 +112,10 @@ class E
116
112
  uri.expand.E
117
113
  end
118
114
 
115
+ def shorten
116
+ uri.shorten.E
117
+ end
118
+
119
119
  def prependURI u
120
120
  E u.to_s + uri
121
121
  end
@@ -129,7 +129,7 @@ class E
129
129
  end
130
130
 
131
131
  def concatURI b
132
- u.appendURI b.E.path
132
+ u.appendURI b.E.shortPath
133
133
  end
134
134
 
135
135
  alias_method :a, :appendURI
@@ -140,6 +140,19 @@ class E
140
140
  uri.path?
141
141
  end
142
142
 
143
+ def shortPath
144
+ @shortPath ||=
145
+ (if path?
146
+ if uri.match /^\//
147
+ uri
148
+ else
149
+ '/' + uri.shorten
150
+ end
151
+ else
152
+ '/E/' + uri.h.dive[0..5] + (Base64.urlsafe_encode64 uri)
153
+ end)
154
+ end
155
+
143
156
  # URI -> path
144
157
  def path
145
158
  @path ||=
@@ -170,15 +183,21 @@ class E
170
183
  end
171
184
 
172
185
  # literals to URIs
173
-
186
+ # currently used for iso8601 dates mapping to paths, so date-range queries can be done w/ just dir/fs tooling
187
+ # could also use as a "trie" for autocomplete or sorting strings
174
188
  def E.literal o
175
189
  E['/'].literal o
176
190
  end
191
+
192
+ Literal={}
193
+ [Purl+'dc/elements/1.1/date',
194
+ Date,DC+'created',DC+'modified',
195
+ ].map{|f|Literal[f]=true}
177
196
 
178
197
  def literal o
179
198
 
180
199
  # already a URI
181
- return self if o.class == E
200
+ return o if o.class == E
182
201
 
183
202
  # blob for non-strings
184
203
  return literalBlob o unless o.class == String
@@ -196,7 +215,7 @@ class E
196
215
 
197
216
  # pathname for short literals
198
217
  def literalURI o
199
- E "/l/"+(Literal[uri] && o.gsub(/[\.:\-T+]/,'/'))+'/'+o
218
+ E "/l/"+o.gsub(/[\.:\-T+]/,'/')+'/'+o if Literal[uri] && o
200
219
  end
201
220
 
202
221
  def literalBlobURI o
@@ -225,13 +244,16 @@ class E
225
244
  {'uri' => uri}
226
245
  end
227
246
 
247
+ # implementation-specific internal pathnames not on the web
248
+ F['/E/GET'] = F[E404]
249
+
228
250
  end
229
251
 
230
252
  class Hash
231
253
  def uri
232
- self["uri"]
254
+ self["uri"]||""
233
255
  end
234
- def url; self.E.url end
256
+ alias_method :url, :uri
235
257
  def label
236
258
  self[E::Label] || uri.label
237
259
  end
@@ -253,18 +275,25 @@ class String
253
275
  self[4..-1]
254
276
  end
255
277
 
256
- # expand qname-style identifier to URI
278
+ # expand qname/CURIE-style identifier to URI
257
279
  Expand={}
258
280
  def expand
259
- # memoize lookups
260
281
  (Expand.has_key? self) ?
261
282
  Expand[self] :
262
283
  (Expand[self] =
263
284
  match(/([^:]+):([^\/].*)/).do{|e|
264
- (E::Abbrev[e[1]]||e[1]+':')+e[2]} ||
285
+ ( E::Prefix[e[1]] || e[1]+':' )+e[2]} ||
265
286
  self )
266
287
  end
267
288
 
289
+ # shrink URI to qname/CURIE/prefix'd identifier
290
+ def shorten
291
+ E::Prefix.map{|p,f|
292
+ return p + ':' + self[f.size..-1] if (index f) == 0
293
+ }
294
+ self
295
+ end
296
+
268
297
  def sh
269
298
  Shellwords.escape self
270
299
  end
@@ -277,29 +306,33 @@ class String
277
306
  # path -> URI || literal
278
307
  def unpath
279
308
 
280
- # URI with scheme
309
+ # HTTP URI
281
310
  if m = (match /^\/([a-z]+:)\/+(.*)/)
282
311
  (m[1] + '//' + m[2]).E
283
312
 
284
- # String literal in store
313
+ # CURIE
314
+ elsif m = (match /^\/([^\/:]+:[^\/]+)/)
315
+ m[1].expand.E
316
+
317
+ # opaque URI w/ optional extension
318
+ elsif match /^\/E\/..\//
319
+ self[9..-1].match(/([^.]+)(.*)/).do{|c|
320
+ (Base64.urlsafe_decode64 c[1]) + c[2]
321
+ }.E
322
+
323
+ # String literal
285
324
  elsif match /^\/E\/blob/
286
325
  self.E.r
287
326
 
288
- # JSON literal in store
327
+ # JSON literal
289
328
  elsif match /^\/E\/json/
290
329
  self.E.r true
291
330
 
292
- # String literal in basename
331
+ # literal in basename
293
332
  elsif match /^\/l\//
294
333
  File.basename self
295
334
 
296
- # URI (opaque)
297
- elsif match /^\/E\/..\//
298
- self[9..-1].match(/([^.]+)(.*)/).do{|c|
299
- (Base64.urlsafe_decode64 c[1]) + c[2]
300
- }.E
301
-
302
- # path
335
+ # plain path
303
336
  else
304
337
  self.E
305
338
  end