infod 0.0.2 → 0.0.3

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