infod 0.0.2 → 0.0.3.0

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 +51 -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 +131 -0
  25. data/infod/{Es/grep.rb → grep.rb} +3 -3
  26. data/infod/{Es/groonga.rb → groonga.rb} +35 -26
  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} +44 -49
  31. data/infod/infod.rb +51 -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 +108 -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} +77 -56
  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} +23 -8
  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} +20 -27
  50. data/infod/{H/time.rb → time.rb} +14 -34
  51. data/infod/{H/wiki.rb → wiki.rb} +0 -0
  52. metadata +53 -64
  53. data/config.ru +0 -3
  54. data/infod/Es.rb +0 -31
  55. data/infod/Es/filter.rb +0 -75
  56. data/infod/Es/glob.rb +0 -22
  57. data/infod/Es/html.rb +0 -271
  58. data/infod/Es/in.rb +0 -68
  59. data/infod/Es/json.rb +0 -68
  60. data/infod/Es/ls.rb +0 -58
  61. data/infod/Es/mail.rb +0 -87
  62. data/infod/Es/mime.rb +0 -59
  63. data/infod/Es/out.rb +0 -52
  64. data/infod/Es/pager.rb +0 -34
  65. data/infod/Es/pdf.rb +0 -19
  66. data/infod/Es/rdf.rb +0 -35
  67. data/infod/H.rb +0 -15
  68. data/infod/H/audio.rb +0 -19
  69. data/infod/H/blog.rb +0 -15
  70. data/infod/H/cal.rb +0 -81
  71. data/infod/H/edit.rb +0 -88
  72. data/infod/H/forum.rb +0 -4
  73. data/infod/H/hf.rb +0 -114
  74. data/infod/H/mail.rb +0 -92
  75. data/infod/H/who.rb +0 -30
  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,108 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ begin
5
+ require 'tmail'
6
+ rescue LoadError => e
7
+ end
8
+
9
+ MessagePath = ->id{
10
+ h = id.h # hash identifier
11
+ '/msg/' + h[0..1] + '/' + h[2] + '/' + id}
12
+
13
+ def triplrTmail &f
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
+ from = m.from[0].to_utf8 # author
19
+ creator = '/m/'+from+'#'+from # author URI
20
+ yield e, DC+'identifier', id # original ID
21
+ yield e, DC+'source', self # original file
22
+ yield e, Type, E[SIOCt + 'MailMessage']
23
+ yield e, Type, E[SIOC + 'Post']
24
+ yield e, Date, m.date.iso8601 if m.date
25
+ yield e, Title, m.subject.to_utf8
26
+ yield e, Creator, E[creator]
27
+ yield e, SIOC+'has_discussion', E[e+'?graph=thread']
28
+
29
+ yield creator, SIOC+'name', m.friendly_from.to_utf8
30
+ yield creator, DC+'identifier', E['mailto:'+from]
31
+ posts = '/m/'+from+'#posts'
32
+ yield creator, SIOC+'creator_of', E[posts]
33
+ yield posts, Type, E[LDP+'Container']
34
+ yield posts, LDP+'firstPage', E['/index/sioc:has_creator/'+CGI.escape(creator)]
35
+
36
+ m.header['x-original-to'].do{|f|
37
+ yield e, SIOC+'reply_to', E[URI.escape "mailto:#{f}?References=<#{e}>&In-Reply-To=<#{e}>&Subject=#{m.subject.to_utf8}"] }
38
+
39
+ %w{to cc bcc}.map{|to|
40
+ m.send(to).do{|to| to.map{|to|
41
+ to = to.to_utf8
42
+ r = '/m/'+to+'#'+to
43
+ yield e, To, E[r]
44
+ yield r, SIOC+'container_of', E['/index/sioc:addressed_to/'+CGI.escape(r)]}}}
45
+
46
+ %w{in_reply_to references}.map{|ref|
47
+ m.send(ref).do{|refs| refs.map{|r|
48
+ yield e, SIOC+'reply_of', E[MessagePath[r[1..-2]]]}}}
49
+
50
+ # RDF:HTML with self-contained minimal styling
51
+ yield e, Content,
52
+ H([{_: :pre, class: :mail, style: 'white-space: pre-wrap',
53
+ c: m.concat_message(e.E,0,&f).gsub(/^\s*(&gt;)(&gt;|\s)*\n/,"").lines.to_a.map{|l| # skip quoted empty-lines
54
+ l.match(/(^\s*(&gt;|On[^\n]+(said|wrote))[^\n]*)\n/) ? {_: :span, class: :q, c: l} : l # wrap quoted lines
55
+ }},
56
+ {_: :style, c: "pre.mail .q {background-color:#00f;color:#fff}\npre.mail a{background-color:#ef3}\npre.mail img {max-width:100%}"}])}
57
+ rescue Exception => e
58
+ puts e
59
+ end
60
+
61
+ def triplrMailMessage &f
62
+ insertDocs :triplrTmail, @r['SERVER_NAME'], [To,SIOC+'has_creator',SIOC+'reply_of'], &f
63
+ end
64
+ =begin
65
+ there's another mail library called Mail, as of v2.5.4 takes 50x as long as tmail (apt-get install ruby-tmail)
66
+ HEAD 200 http://m/m/2013/12/01/?nocache=&triplr=triplrMail curl/7.33.0 5.4003388
67
+ HEAD 200 http://m/m/2013/12/01/?nocache=&triplr=triplrTmail curl/7.33.0 0.1198720
68
+
69
+ almost a copy of above works but identifiers are not wrapped in <> - with caching it might be fast enough..
70
+
71
+ =end
72
+
73
+ end
74
+
75
+ module TMail
76
+ class Mail
77
+ def unicode_body
78
+ unquoted_body.to_utf8
79
+ end
80
+ def concat_message i, partCount=0, &f
81
+ if multipart?
82
+ parts.map{|part|
83
+ if part.multipart? # and even more nested parts..
84
+ part.concat_message i, partCount, &f
85
+ elsif !attachment?(part) && part.sub_type != 'html'
86
+ part.unicode_body.hrefs true
87
+ else # attachment
88
+ a = i.a('.attache').mk # create container
89
+ p = a.as(part['content-type']['name'] || (partCount.to_s + '.' + (E::MIME.invert[part.content_type] || '.bin').to_s))
90
+ p.w part.body if !p.e # write attachment into message container
91
+ partCount += 1 # display images
92
+ yield i.uri, E::SIOC+'attachment', p
93
+ '<a href="'+p.uri+'">'+(part.main_type=='image' ? '<img src="'+p.uri+'">' : '')+p.uri.label+"</a><br>\n"
94
+ end
95
+ }.join
96
+ else # just a part
97
+ unicode_body.do{|b|
98
+ if content_type && content_type.match(/html/)
99
+ (b.split /<body[^>]*>/)[-1].split(/<\/body>/)[0]
100
+ else
101
+ b.hrefs true
102
+ end}
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ 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,20 +31,7 @@ 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 }
44
- end
45
-
46
- # same as above, but w/ URI errors
47
- def docBaseURI
48
- u = URI uri
49
- s = u.scheme
50
- p = u.path
51
- p = '/' if p.empty?
52
- ((s ? s + '://' : '') + # scheme
53
- u.host + # host
54
- File.dirname(p).t + # path
55
- File.basename(p)[0..-(File.extname(p).size+1)]).E # doc
34
+ !uri.empty? && uri.split(/#/)[0].do{|u|u.E.do{|d| d.dirname.as d.bare }} || E['']
56
35
  end
57
36
 
58
37
  def frag
@@ -79,22 +58,27 @@ class E
79
58
  end
80
59
  alias_method :dir, :dirname
81
60
 
82
- # local URL from unlocatable identifier (mail MSGID, etc)
83
- def url
84
- path? ? uri : Prefix + (CGI.escape uri)
61
+ # add hostname to URI if missing
62
+ def hostURL e
63
+ host = 'http://'+e['SERVER_NAME']
64
+ if uri.index('/') == 0
65
+ host + uri
66
+ else
67
+ uri
68
+ end
85
69
  end
86
70
 
87
- # local URL even if locatable-identifier
71
+ # pointer to local data about global URI
88
72
  def localURL e
89
73
  # path
90
- if uri.index('/') == 0
74
+ if uri.index('/') == 0 # already a local path
91
75
  uri
92
76
  # host match
93
- elsif uri.index('http://'+e['SERVER_NAME']+'/') == 0
77
+ elsif e && uri.index('http://'+e['SERVER_NAME']+'/') == 0
94
78
  pathSegment.uri
95
79
  # non-local
96
80
  else
97
- Prefix + (CGI.escape uri)
81
+ URIURL + (CGI.escape uri)
98
82
  end
99
83
  end
100
84
 
@@ -116,6 +100,10 @@ class E
116
100
  uri.expand.E
117
101
  end
118
102
 
103
+ def shorten
104
+ uri.shorten.E
105
+ end
106
+
119
107
  def prependURI u
120
108
  E u.to_s + uri
121
109
  end
@@ -129,7 +117,7 @@ class E
129
117
  end
130
118
 
131
119
  def concatURI b
132
- u.appendURI b.E.path
120
+ u.appendURI b.E.shortPath
133
121
  end
134
122
 
135
123
  alias_method :a, :appendURI
@@ -140,6 +128,19 @@ class E
140
128
  uri.path?
141
129
  end
142
130
 
131
+ def shortPath
132
+ @shortPath ||=
133
+ (if path?
134
+ if uri.match /^\//
135
+ uri
136
+ else
137
+ '/' + uri.shorten
138
+ end
139
+ else
140
+ '/E/' + uri.h.dive[0..5] + (Base64.urlsafe_encode64 uri)
141
+ end)
142
+ end
143
+
143
144
  # URI -> path
144
145
  def path
145
146
  @path ||=
@@ -170,15 +171,21 @@ class E
170
171
  end
171
172
 
172
173
  # literals to URIs
173
-
174
+ # currently used for iso8601 dates mapping to paths, so date-range queries (depth-first subtrees) can be done w/ dir/fs tools
175
+ # could also use as a "trie" for autocomplete + sorted-strings
174
176
  def E.literal o
175
177
  E['/'].literal o
176
178
  end
179
+
180
+ Literal={}
181
+ [Purl+'dc/elements/1.1/date',
182
+ Date,DC+'created',DC+'modified',
183
+ ].map{|f|Literal[f]=true}
177
184
 
178
185
  def literal o
179
186
 
180
187
  # already a URI
181
- return self if o.class == E
188
+ return o if o.class == E
182
189
 
183
190
  # blob for non-strings
184
191
  return literalBlob o unless o.class == String
@@ -196,7 +203,7 @@ class E
196
203
 
197
204
  # pathname for short literals
198
205
  def literalURI o
199
- E "/l/"+(Literal[uri] && o.gsub(/[\.:\-T+]/,'/'))+'/'+o
206
+ E "/l/"+o.gsub(/[\.:\-T+]/,'/')+'/'+o if Literal[uri] && o
200
207
  end
201
208
 
202
209
  def literalBlobURI o
@@ -225,13 +232,16 @@ class E
225
232
  {'uri' => uri}
226
233
  end
227
234
 
235
+ # implementation-specific internal pathnames not on the web
236
+ F['/E/GET'] = F[E404]
237
+
228
238
  end
229
239
 
230
240
  class Hash
231
241
  def uri
232
- self["uri"]
242
+ self["uri"]||""
233
243
  end
234
- def url; self.E.url end
244
+ alias_method :url, :uri
235
245
  def label
236
246
  self[E::Label] || uri.label
237
247
  end
@@ -253,18 +263,25 @@ class String
253
263
  self[4..-1]
254
264
  end
255
265
 
256
- # expand qname-style identifier to URI
266
+ # expand qname/CURIE-style identifier to URI
257
267
  Expand={}
258
268
  def expand
259
- # memoize lookups
260
269
  (Expand.has_key? self) ?
261
270
  Expand[self] :
262
271
  (Expand[self] =
263
272
  match(/([^:]+):([^\/].*)/).do{|e|
264
- (E::Abbrev[e[1]]||e[1]+':')+e[2]} ||
273
+ ( E::Prefix[e[1]] || e[1]+':' )+e[2]} ||
265
274
  self )
266
275
  end
267
276
 
277
+ # shrink URI to qname/CURIE/prefix'd identifier
278
+ def shorten
279
+ E::Prefix.map{|p,f|
280
+ return p + ':' + self[f.size..-1] if (index f) == 0
281
+ }
282
+ self
283
+ end
284
+
268
285
  def sh
269
286
  Shellwords.escape self
270
287
  end
@@ -277,29 +294,33 @@ class String
277
294
  # path -> URI || literal
278
295
  def unpath
279
296
 
280
- # URI with scheme
297
+ # HTTP URI
281
298
  if m = (match /^\/([a-z]+:)\/+(.*)/)
282
299
  (m[1] + '//' + m[2]).E
283
300
 
284
- # String literal in store
301
+ # CURIE
302
+ elsif m = (match /^\/([^\/:]+:[^\/]+)/)
303
+ m[1].expand.E
304
+
305
+ # opaque URI w/ optional extension
306
+ elsif match /^\/E\/..\//
307
+ self[9..-1].match(/([^.]+)(.*)/).do{|c|
308
+ (Base64.urlsafe_decode64 c[1]) + c[2]
309
+ }.E
310
+
311
+ # String literal
285
312
  elsif match /^\/E\/blob/
286
313
  self.E.r
287
314
 
288
- # JSON literal in store
315
+ # JSON literal
289
316
  elsif match /^\/E\/json/
290
317
  self.E.r true
291
318
 
292
- # String literal in basename
319
+ # literal in basename
293
320
  elsif match /^\/l\//
294
321
  File.basename self
295
322
 
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
323
+ # plain path
303
324
  else
304
325
  self.E
305
326
  end