infod 0.0.1

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 (67) hide show
  1. data/bin/infod +37 -0
  2. data/config.ru +3 -0
  3. data/infod/Es/fs.rb +154 -0
  4. data/infod/Es/groonga.rb +101 -0
  5. data/infod/Es/redis.rb +3 -0
  6. data/infod/Es/sqlite.rb +3 -0
  7. data/infod/Es.rb +67 -0
  8. data/infod/H.rb +29 -0
  9. data/infod/K.rb +197 -0
  10. data/infod/N.rb +248 -0
  11. data/infod/Rb.rb +71 -0
  12. data/infod/Th/404.rb +55 -0
  13. data/infod/Th/500.rb +10 -0
  14. data/infod/Th/GET.rb +132 -0
  15. data/infod/Th/HEAD.rb +5 -0
  16. data/infod/Th/PATCH.rb +5 -0
  17. data/infod/Th/POST.rb +19 -0
  18. data/infod/Th/local.rb +22 -0
  19. data/infod/Th/uid.rb +24 -0
  20. data/infod/Th.rb +110 -0
  21. data/infod/W/audio.rb +56 -0
  22. data/infod/W/blog.rb +3 -0
  23. data/infod/W/cal.rb +110 -0
  24. data/infod/W/chat.rb +81 -0
  25. data/infod/W/color.rb +28 -0
  26. data/infod/W/core.rb +77 -0
  27. data/infod/W/css.rb +24 -0
  28. data/infod/W/csv.rb +13 -0
  29. data/infod/W/du.rb +35 -0
  30. data/infod/W/edit.rb +8 -0
  31. data/infod/W/examine/examine.rb +59 -0
  32. data/infod/W/examine/exhibit.rb +34 -0
  33. data/infod/W/examine/hist.rb +55 -0
  34. data/infod/W/examine/history.rb +19 -0
  35. data/infod/W/examine/normal.rb +31 -0
  36. data/infod/W/examine/protovis.rb +30 -0
  37. data/infod/W/examine/sw.rb +114 -0
  38. data/infod/W/examine/time/graph.rb +86 -0
  39. data/infod/W/examine/time/line.rb +24 -0
  40. data/infod/W/feed.rb +116 -0
  41. data/infod/W/find.rb +24 -0
  42. data/infod/W/forum.rb +3 -0
  43. data/infod/W/grep.rb +27 -0
  44. data/infod/W/html.rb +143 -0
  45. data/infod/W/image.rb +61 -0
  46. data/infod/W/json.rb +44 -0
  47. data/infod/W/kv.rb +66 -0
  48. data/infod/W/ls.rb +50 -0
  49. data/infod/W/mail.rb +248 -0
  50. data/infod/W/page.rb +30 -0
  51. data/infod/W/pdf.rb +16 -0
  52. data/infod/W/post.rb +9 -0
  53. data/infod/W/rdf.rb +32 -0
  54. data/infod/W/schema.rb +172 -0
  55. data/infod/W/search.rb +33 -0
  56. data/infod/W/shell.rb +30 -0
  57. data/infod/W/source.rb +35 -0
  58. data/infod/W/table.rb +87 -0
  59. data/infod/W/text.rb +94 -0
  60. data/infod/W/tree.rb +26 -0
  61. data/infod/W/vfs.rb +175 -0
  62. data/infod/W/wiki.rb +18 -0
  63. data/infod/W.rb +34 -0
  64. data/infod/Y.rb +17 -0
  65. data/infod/infod.rb +13 -0
  66. data/infod.rb +13 -0
  67. metadata +129 -0
data/infod/W/kv.rb ADDED
@@ -0,0 +1,66 @@
1
+ class E
2
+
3
+ # a simple key/value RDF store on a fs
4
+
5
+ # *get*
6
+ # (E'http://www.kanzaki.com/ns/music#EnglishHorn')[RDFs+'comment']
7
+ # -> ["A double-reed woodwind instrument, larger member of the oboe family."]
8
+ #
9
+ # *set*
10
+ # (E'lement')['level']='trace'
11
+ #
12
+ # *update*
13
+ # (E'lement')['level','trace','abundant']
14
+ #
15
+ def [] p,o=nil, v=nil
16
+ unless o
17
+ (s p).listPredicates
18
+ else
19
+ edit E(p),(o.class == E ? o : E(p).literal(o)),v
20
+ end
21
+ end
22
+
23
+ def []= p,o
24
+ self[p,o]
25
+ end
26
+
27
+ def edit p,o,v=nil
28
+ d=(s p).s o # object
29
+ if v # edit
30
+ if d.e
31
+ d.deleteNode # remove
32
+ indexEdit p,o,'' # unindex
33
+ end
34
+ self[p,v] unless v.empty? # add
35
+ else
36
+ unless d.e
37
+ indexEdit p,o,nil # index add
38
+ d.dir # create
39
+ end
40
+ end
41
+ touch if e
42
+ end
43
+
44
+ def triplrFsStore
45
+ listPredicates.map{|p|
46
+ self[p].map{|o|
47
+ yield uri, p.uri, o }}
48
+ end
49
+
50
+ def deletePredicate p
51
+ self[p].each{|o| self[p,o,'']}
52
+ end
53
+
54
+ # property list
55
+ # E -> [E]
56
+ def listPredicates
57
+ s = u.to_s.size+1
58
+ subtree.map{|n|n.uri[s..-1].unpath}
59
+ end
60
+
61
+ def literalBlob o
62
+ u = literalBlobURI o
63
+ u.w o,!o.class==String unless u.f
64
+ end
65
+
66
+ end
data/infod/W/ls.rb ADDED
@@ -0,0 +1,50 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ fn 'set/ls',->d,e,m{d.c}
5
+
6
+ # filesystem metadata only
7
+ fn 'graph/ls',->d,e,m{d.c.map{|c|c.fromStream m, :triplrInode, false}}
8
+
9
+ # basic directory view
10
+ fn 'view/dir',->i,e{
11
+
12
+ # localize URL
13
+ h = 'http://' + e['SERVER_NAME'] + '/'
14
+ l = -> u {
15
+ if u.index(h) == 0
16
+ u # already a local link
17
+ else
18
+ # generate local link
19
+ Prefix + u
20
+ end}
21
+
22
+ # item thumbnail / link
23
+ a = -> i { e = i.E
24
+ {_: :a, href: l[e.uri],
25
+ c: e.uri.match(/(gif|jpe?g|png)$/i) ? {_: :img, src: i.uri+'?233x233'} :
26
+ e.uri.sub(/.*\//,'')
27
+ }}
28
+
29
+ [(H.once e, 'dir', (H.css '/css/ls')),
30
+ i.map{|u,r| r[Posix+'dir#child'] ? # directory?
31
+ {class: :dir, style: "background-color: #{E.c}", # dir wrapper
32
+ c: [{c: [{_: :a, href: l[r.uri]+'?graph=ls&view=ls', c: r.uri.sub( 'http://'+e['SERVER_NAME'],'')}, # link to ls
33
+ {_: :a, href: l[r.uri].t, c: '/'}]},
34
+ r[Posix+'dir#child'].map{|c|a[c]}]} : # children
35
+ a[r]}]} # item
36
+
37
+ F['view/'+MIMEtype+'inode/directory']=F['view/dir']
38
+
39
+ # tabular rendering
40
+ fn 'view/ls',->i,e{
41
+ [(H.css '/css/ls'),
42
+ {_: :a, class: :up, href: E(e['uri']).parent.url+'?graph=ls&view=ls', c: '↑'},
43
+ {class: :ls,
44
+ c: (Fn 'view/tab',i,e)},
45
+ {_: :a, class: :du, href: e['REQUEST_PATH'].t+'??=du', c: :du, rel: :nofollow},
46
+ (Fn 'view/find',i,e),'<br clear=all>',
47
+ {_: :a, class: :down, href: e['uri'].E.url.t, c: '&darr;'},
48
+ ]}
49
+
50
+ end
data/infod/W/mail.rb ADDED
@@ -0,0 +1,248 @@
1
+ # -*- coding: utf-8 -*-
2
+ #watch __FILE__
3
+
4
+ class E
5
+
6
+ # qs alias section
7
+ F["?"] ||= {}
8
+ F["?"].update({
9
+ 'thread' =>{'graph'=>'thread',
10
+ 'sort' => 'dc:date',
11
+ 'reverse' => nil,
12
+ 'view' => 'multi',
13
+ 'views' => 'timegraph,mail',
14
+ 'arc' => '/parent',
15
+ 'label' => 'sioc:name'},
16
+ 'ann' =>{'view'=>'threads',
17
+ 'matchP' => 'dc:title',
18
+ 'match' => /[^a-zA-Z][Aa][Nn][nN]([oO][uU]|[^a-zA-Z])/}})
19
+
20
+ def triplrMail; require 'tmail'
21
+
22
+ # read message
23
+ i = -> i {E i[1..-2]} # Message-ID -> E
24
+ (TMail::Mail.load node).do{|m| # parse
25
+ d = m.message_id; return unless d # parse successful?
26
+ e = i[d] # Message resource
27
+ e.e || ( # Message-ID locatable?
28
+
29
+ # index previously unseen mail
30
+ ln e # create message-id path
31
+ %w{in_reply_to references}.map{|p| # reference arcs
32
+ m.send(p).do{|o| o.map{|o| # lookup references
33
+ e.index SIOC+'reply_of', i[o]}}} # index references
34
+ e.ln '/' + m.date.iso8601[0..12].gsub(/[^\d]/,'/') + '/' + e.uri # date/hour index tree
35
+ self.index Creator, m.from[0].E # index From
36
+ self.index To, m.to[0].E ) # index To
37
+
38
+ # yield triples
39
+ yield e.uri, Type, E[SIOCt+'MailMessage']
40
+ yield e.uri, Date, m.date.iso8601 if m.date
41
+ yield e.uri, Content, m.decentBody
42
+ [[:subject,Title],
43
+ [:to,To,true],
44
+ [:cc,To,true],
45
+ [:bcc,To,true],
46
+ [:friendly_from,SIOC+'name'],
47
+ [:from,Creator,true],
48
+ [:reply_to,'/mail/reply_to',true],
49
+ [:in_reply_to,'/parent',true,true],
50
+ [:in_reply_to,SIOC+'reply_of',true,true],
51
+ [:references,SIOC+'reply_of',true,true],
52
+ ].each{|a| m.send(a[0]).do{|o| [*o].map{|o|
53
+ yield e.uri,a[1], # skip empty String values
54
+ (a[2] ? (a[3] ? i[o] : o.E) : o.to_utf8) unless o.match(/\A[, \n]*\Z/)}}}}
55
+
56
+ rescue Exception => e
57
+ puts [:mail,uri,e].join(' ')
58
+ end
59
+
60
+ fn 'graph/thread',->d,_,m{d.walk SIOC+'reply_of',m}
61
+
62
+ # overview of a message set
63
+ fn 'view/threads',->d,env{
64
+
65
+ # occurrence-count statistics
66
+ g = {}
67
+ d.map{|_,m|
68
+ m[To].map{|t|
69
+ g[t.uri]||=0
70
+ g[t.uri]=g[t.uri].succ}}
71
+
72
+ # CSS
73
+ [(H.css '/css/mail'),
74
+
75
+ ([{_: :a, href: '/@'+env.q['p']+'?set=indexP&view=page&v=linkPO&c=12', c: env.q['p']},
76
+ {_: :a, href: '/m?y=day', c: ' :: '},
77
+ {_: :a, href: E[env['uri']].url+'?set=indexPO&view=page&v=threads&c=32&p='+env.q['p'], c: env['uri']}
78
+ ] if env.q['set']=='indexPO'),
79
+
80
+ '<table>',
81
+
82
+ # subgroup by title
83
+ d.values.group_by{|r|[*r[Title]][0].sub(/^[rR][eE][^A-Za-z]./,'')}.
84
+
85
+ # group by recipient
86
+ group_by{|r,k|
87
+
88
+ # show most-popular first
89
+ k[0].do{|k|
90
+ k[To].do{|o|o.sort_by{|t|g[t.uri]}.reverse.head.uri}}}.
91
+
92
+ # display
93
+ map{|e|
94
+ # recipient-group color
95
+ c = '#%06x' % rand(16777216)
96
+ ['<tr><td class=subject>',
97
+
98
+ # show most-popular groups first
99
+ e[1].sort_by{|m|m[1].size}.reverse.map{|t|
100
+
101
+ # link to thread
102
+ [{_: :a, property: Title, :class => 'thread', style: "border-color:#{c}", href: t[1][0].url+'??=thread',
103
+ c: t[0].to_s.gsub(/[<>]/,'_').gsub(/\[([a-z\-A-Z0-9]+)\]/,'<span class=g>\1</span>')},
104
+
105
+ # link to individual messages
106
+ (t[1].size > 1 &&
107
+ ['<br>', t[1].map{|s|
108
+
109
+ # author name and RDFa
110
+ [{_: :a, property: Creator, href: s.url+'??=thread#'+s.uri, :class => 'sender', style: 'background-color:'+c,
111
+ c: s[SIOC+'name'][0].split(/\W/,2)[0]},' ']}]),'<br>']},'</td>',
112
+
113
+ # recipient group, Mailing List
114
+ {_: :td, class: :group, property: To,
115
+ c: {_: :a, :class => :to, style: 'background-color:'+c, c: e[0] && e[0].split(/@/)[0],
116
+ href: e[0] && e[0].E.url+'?set=indexPO&p=sioc:addressed_to&view=page&v=threads'}},
117
+
118
+ '</tr>']},'</table>',
119
+
120
+ # link to show full content of entire message-set
121
+ {_: :a, id: :down, c: '&darr;',href: env['REQUEST_PATH']+env.q.merge({'view'=>'page','views'=>'timegraph,mail','v'=>'multi'}).qs}]}
122
+
123
+
124
+ # show a set of messages
125
+ fn 'view/mail',->d,e{
126
+ title = nil
127
+
128
+ # JS/CSS dependencies
129
+ [(H.once e,'mail.js',
130
+ (H.css '/css/mail'),
131
+ (H.js '/js/mail'),
132
+ (H.once e,:mu,(H.js '/js/mu')),
133
+
134
+ # up to set-overview
135
+ ({_: :a, id: :up, href: e['REQUEST_PATH'] + e.q.merge({'view' => 'page', 'v' => 'threads'}).qs, c: '&uarr;'} if d.keys.size > 2),
136
+
137
+ # collapse/expand quoted content
138
+ {id: :showQuote, c: :quote, show: :true},{_: :style, id: :quote}),
139
+
140
+ # each message
141
+ d.values.map{|m|
142
+
143
+ # content available?
144
+ [m.class == Hash && (m.has_key? E::SIOC+'content') &&
145
+
146
+ {:class => :mail,
147
+
148
+ c: [# link to self
149
+ {_: :a, name: m.uri, href: m.url, rel: :permalink, title: :link, c: ' '},
150
+
151
+ # To:, From: index search links
152
+ [['sioc:has_creator',Creator],['sioc:addressed_to',To]].map{|a|
153
+ m[a[1]].do{|m|
154
+ m.map{|f| f.respond_to?(:uri) &&
155
+ {_: :a, property: a[0], href: f.url+'?set=indexPO&p='+a[0]+'&view=page&views=timegraph,mail&v=multi&c=8', c: f.uri}}}},
156
+
157
+ # mailto URI with embedded reply metadata
158
+ (m['/mail/reply_to']||m[Creator]).do{|r| r[0] && r[0].respond_to?(:uri) &&
159
+ {_: :a, title: :reply, c: 'r',
160
+ href: "mailto:#{r[0].uri}?References=<#{m.uri}>&In-Reply-To=<#{m.uri}>&Subject=#{m[Title].join}"}},'<br clear=all>',
161
+
162
+ # content
163
+ {_: :pre,
164
+ c: m[Content].map{|b|
165
+
166
+ # line count
167
+ i = 0
168
+
169
+ # HTML message content
170
+ b.class==String && b.
171
+
172
+ # erase empty quoted lines
173
+ gsub(/^\s*(&gt;)(&gt;|\s)*\n/,"").
174
+
175
+ # each line
176
+ lines.to_a.map{|l|
177
+
178
+ # line identifier
179
+ f = m.uri + ':' + (i+=1).to_s
180
+
181
+ # wrapper
182
+ {_: :span,
183
+
184
+ # is line quoted?
185
+ class: ((l.match /(^\s*(&gt;|On[^\n]+(said|wrote))[^\n]*)\n/) ? 'q' : 'u'), c:
186
+
187
+ # id
188
+ [{_: :a, id: f},
189
+
190
+ # line
191
+ l.chomp,
192
+
193
+ # link
194
+ (l.size > 64 &&
195
+ {_: :a, class: :line, href: '#'+f,c: '↵'}),
196
+
197
+ "\n" ]}}}}, # collate lines
198
+
199
+ # title
200
+ m[Title].do{|t|
201
+ # only show if changed from previous
202
+ title != t[0] && (
203
+ title = t[0] # update title
204
+ [{:class => :title, c: t.html, _: :a, href: m.url+'??=thread#'+m.uri, style: "color: #{E.c}"},
205
+ '<br clear=all>'])}]}]}]}
206
+
207
+ # set a default view for MIME and SIOC types
208
+ [MIMEtype+'message/rfc822',
209
+ SIOCt+'MailMessage'].
210
+ map{|m| F['view/'+m] = F['view/mail'] }
211
+
212
+ end
213
+
214
+ module TMail
215
+ class Mail
216
+ def unicode_body
217
+ unquoted_body.to_utf8
218
+ end
219
+ def decentBody
220
+ unHTML=->t{t.split(/<body[^>]*>/)[-1].split(/<\/body>/)[0]}
221
+ if multipart?
222
+ parts.collect{ |part|
223
+ c = part["content-type"]
224
+ if part.multipart?
225
+ part.decentBody
226
+ elsif header.nil?
227
+ ""
228
+ elsif !attachment?(part) && c.sub_type != 'html'
229
+ part.unicode_body.hrefs(true)
230
+ else
231
+ (c["name"]||'attach').do{|a|
232
+ message_id ? (message_id[1..-2]+'/'+a).E.do{|i|
233
+ i.w part.body if !i.e
234
+ '<a href='+i.url+'>'+(part.main_type=='image' ? '<img src="'+i.url+'">' : a)+"</a><br>\n"
235
+ } : ""};end
236
+ }.join
237
+
238
+ else
239
+ unicode_body.do{|b|content_type&&content_type.match(/html/) ? unHTML.(b) : b.hrefs(true)}
240
+ end
241
+ rescue
242
+ ''
243
+ end
244
+ end
245
+ end
246
+
247
+ # TMail calls this nonexistent method on strings
248
+ class String; def is_binary_data?; true; end; end
data/infod/W/page.rb ADDED
@@ -0,0 +1,30 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ fn 'head/page',->d,e{ v = e.q['v']
5
+ Fn 'head' + (v != 'page' && '/' + v), d, e}
6
+
7
+ fn 'view/page',->d,e{
8
+ # try daydirs if no pagination data provided
9
+ !d.has_any_key(%w{next prev}) &&
10
+ e['REQUEST_PATH'].match(/(.*?\/)([0-9]{4})\/([0-9]{2})\/([0-9]{2})(.*)/).do{|m|
11
+ t = ::Date.parse "#{m[2]}-#{m[3]}-#{m[4]}"
12
+ d['prev'] = {'uri' => 'prev','url' => m[1]+(t-1).strftime('%Y/%m/%d')+m[5]}
13
+ d['next'] = {'uri' => 'next','url' => m[1]+(t+1).strftime('%Y/%m/%d')+m[5]}}
14
+
15
+ # links
16
+ c=[d['prev'].do{|p| d.delete('prev') # prev
17
+ {_: :a, rel: :prev, style: 'float:left; font-size:2em',
18
+ href: [*p['url']][0]+e.q.merge(p).except('uri','url').qs,
19
+ title: (p['b']||p['url']), c: '&larr;'}},
20
+
21
+ d['next'].do{|n| d.delete('next') # next
22
+ {_: :a, rel: :next, style: 'float:right;font-size:2em',
23
+ href: [*n['url']][0]+e.q.merge(n).except('uri','url').qs,
24
+ title: (n['b']||n['url']), c: '&rarr;'}}]
25
+
26
+ [(H.js '/js/pager'),(H.once e,:mu,(H.js '/js/mu')), # n/p key shortcuts
27
+ c,(H (F['view/'+e.q['v']]||F['view']).(d,e)), # content
28
+ '<br clear=all>',c]}
29
+
30
+ end
data/infod/W/pdf.rb ADDED
@@ -0,0 +1,16 @@
1
+ class E
2
+
3
+ F["?"]||={}
4
+ F["?"].update({'pdf'=>
5
+ {'filter'=>'p',
6
+ 'p'=>'uri,Author,Title,Producer,dc:date,stat:size',
7
+ 'sort'=>'Producer',
8
+ 'view'=>'tab'
9
+ }})
10
+
11
+ def triplrPDF &f
12
+ yield uri,Content,`pdftotext #{sh}; cat #{docBase.a('.txt').sh}`
13
+ dateNorm :triplrStdOut,'pdfinfo', &f
14
+ end
15
+
16
+ end
data/infod/W/post.rb ADDED
@@ -0,0 +1,9 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ F['view/'+SIOC+'Post']=->g,r{
5
+ g.map{|u,r|
6
+ r[Content].do{|c|
7
+ [r.html]}}}
8
+
9
+ end
data/infod/W/rdf.rb ADDED
@@ -0,0 +1,32 @@
1
+ class E
2
+
3
+ def self.requireRDF; %w{n3 rdfa rdfxml turtle}.map{|r|require 'rdf/'+r}; require 'json/ld' end
4
+
5
+ def self.renderRDF d,f=:ntriples; E.requireRDF
6
+ RDF::Writer.for(f).buffer{|w|
7
+ d.values.each{|r|
8
+ r.triples{|s,p,o|
9
+ # puts :s,s,:p,p,:o,o.class,o
10
+ w << RDF::Statement.new(RDF::URI(s),RDF::URI(p),
11
+ (o.class==Hash||o.class==E) ?
12
+ RDF::URI(o.uri) :
13
+ RDF::Literal(o))}}}
14
+ rescue Exception => e
15
+ puts [:RDF,d.keys[0..8],f,e].join ' '
16
+ end
17
+
18
+ def triplrRDFformats t=nil
19
+ E.requireRDF
20
+ (t == :rdfa ? RDF::RDFa : RDF)::Reader.
21
+ open(e ? d : uri, :format => t){|r|
22
+ r.each_triple{|s,p,o|
23
+ yield s.to_s, p.to_s,
24
+ ((o.class==RDF::Node || o.class==RDF::URI) ? o.to_s.E :
25
+ o.value.do{|v|
26
+ v.class == String ? v.to_utf8 : v})}}
27
+ self
28
+ rescue Exception => e
29
+ puts [:RDF,uri,e].join ' '
30
+ end
31
+
32
+ end
data/infod/W/schema.rb ADDED
@@ -0,0 +1,172 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ # install schema-cache
5
+ def E.schemaCache
6
+ E.schemaDocs.map &:schemaCache
7
+ end
8
+ def E.schemaUncache
9
+ E.schemaDocs.map &:schemaUncache
10
+ end
11
+
12
+ def schemaCache
13
+ schemaCacheDoc
14
+ schemaIndexDoc
15
+ end
16
+ def schemaUncache
17
+ schemaUnindexDoc
18
+ schemaUnlinkSlashURIs
19
+ schemaUncacheDoc
20
+ end
21
+
22
+ # cache schema docs
23
+ def schemaCacheDoc
24
+ if ttl.e || ef.e # already cached?
25
+ print "c "
26
+ else
27
+ ttl.w(`rapper -o turtle #{uri}`) # write turtle
28
+ end
29
+ end
30
+
31
+ def schemaUncacheDoc
32
+ ef.deleteNode # remove JSON
33
+ ttl.deleteNode # remove Turtle
34
+ end
35
+
36
+ # index schema docs
37
+ def schemaIndexDoc
38
+ c = E.schemaStatistics
39
+ if (nt.e || # skip already-processed docs
40
+ ttl.do{|d|d.e && d.size > 256e3}) # skip huge dbpedia/wordnet dumps
41
+ print "e "
42
+ else
43
+ g = graph # schema graph
44
+ ttl.deleteNode # convert Turtle
45
+ ef.w g,true if !ef.e# to JSON (for faster loading)
46
+ roonga "schema" # index in rroonga
47
+ m={}; puts uri # statistics graph
48
+ g.map{|u,_| # each resource
49
+ c[u] && # do stats exist?
50
+ m[u] = {'uri'=>u, '/frequency' => c[u]}} # add to graph
51
+ nt.w E.renderRDF m # store N-triples
52
+ schemaLinkSlashURIs # link "Slash-URI" resources to definer
53
+ end
54
+ end
55
+
56
+ def schemaUnindexDoc
57
+ unroonga
58
+ nt.deleteNode
59
+ end
60
+
61
+ # make slash-URIs resolvable
62
+ def schemaLinkSlashURIs undo=false
63
+ return if !ef.e # cache populated?
64
+ graph.do{|m| # build graph
65
+ m.map{|u,r| # iterate through URIs
66
+ r[RDFs+'isDefinedBy'].do{|d| # check for DefinedBy attribute
67
+ t = u.E.ef # symlink location
68
+ t.dirname.dir # container dir of symlink
69
+ if undo
70
+ if t.e
71
+ t.deleteNode # remove link
72
+ puts "-#{t}"
73
+ end
74
+ else
75
+ unless t.e
76
+ ef.ln t # add link
77
+ puts "#{t} -> #{ef}"
78
+ end
79
+ end
80
+ }}}
81
+ rescue Exception => e
82
+ puts e
83
+ end
84
+
85
+ def schemaUnlinkSlashURIs
86
+ schemaLinkSlashURIs :undo
87
+ end
88
+
89
+ # parse gromgull's BTC statistics
90
+ def E.schemaStatistics
91
+ @gromgull ||=
92
+ (data = '/predicates.2010'.E
93
+ (puts "curl http://data.whats-your.name/schema/gromgull.gz | zcat > predicates.2010"; exit) unless data.e
94
+ # occurrence count :: URI -> int
95
+ usage = {}
96
+ data.read.each_line{|e|
97
+ e.match(/(\d+)[^<]+<([^>]+)>/).do{|r|
98
+ usage[r[2]] = r[1].to_i }}
99
+ usage)
100
+ end
101
+
102
+ # parse schema URIs
103
+ def E.schemaDocs
104
+ @docs ||=
105
+ (source = E['http://prefix.cc/popular/all.file.txt']
106
+ mirror = E['http://localhost/css/i/prefix.cc.txt']
107
+ schemae = (mirror.e ? mirror : source).
108
+ read.split("\n"). # each doc
109
+ grep(/^[^#]/). # skip commented
110
+ map{|t|t.split(/\t/)[1].E}) # URI field
111
+ end
112
+
113
+ fn '/schema/GET',->e,r{
114
+ r.q.merge!({
115
+ 'graph'=>'roonga',
116
+ 'context'=>'schema',
117
+ 'view'=>'search',
118
+ 'filter'=>'frag',
119
+ 'v'=>'schema',
120
+ 'c'=>(r.q.has_key?('q') ? 1000 : 0)
121
+ })
122
+ e.response
123
+ }
124
+
125
+ fn 'u/schema/weight',->d,e{
126
+ q = e.q['q']
127
+ d.keys.map{|k| k.class==String && d[k].class==Hash &&
128
+ (s=0
129
+ u=k.downcase
130
+ d[k]['/frequency'][0].to_i.do{|f|f > 0 && (s=s + (Math.log f))}
131
+ s=s+(u.label.match(q.downcase) && 6 ||
132
+ q.camelToke.map(&:downcase).map{|c|
133
+ u.match(c) && 3 || 0}.sum)
134
+ d[k]['score'] = s )}}
135
+
136
+ fn 'view/schema',->d,e{
137
+ # score resources on popularity, URL friendliness
138
+ Fn 'u/schema/weight',d,e
139
+ # sort updated response-graph based on score
140
+ d = d.select{|u,r|
141
+ r['score'] && r['score'].respond_to?(:>)
142
+ }.sort_by{|u,r| r['score'] }.reverse
143
+
144
+ d.size > 0 &&
145
+ (# fit values to CSS range
146
+ scale = 255 / d[0][1]['score'].do{|s|s > 0 && s || 1}
147
+ [(H.css '/css/schema'),'<table>',
148
+ d.map{|u,r|
149
+ # score -> normalized score
150
+ v = r['score'] * scale
151
+ # score -> greyscale value
152
+ f = '%02x' % v
153
+ # greyscale val -> full CSS
154
+ style = 'color:#'+(v > 128 ? '000' : 'fff')+';background-color:#'+f+f+f
155
+ # stats on stats
156
+ title = r['/frequency'][0].to_s + ' | %.3f'%r['score']
157
+
158
+ [{_: :tr, class: :overview, style: style, title: title,
159
+ c: [{_: :td, class: :identity,
160
+ c: u.E.html},
161
+ {_: :td, class: :label,
162
+ c: [{_: :span, class: :stats, c: title},
163
+ r[RDFs+'label'][0].do{|l|
164
+ {_: :a, href: r.uri,class: :label,c: l}}]}]},
165
+ {_: :tr, class: :details, style: style, title: title,
166
+ c: {_: :td, colspan: 2, class: :describe,
167
+ c: [r[RDFs+'comment'][0].do{|l|
168
+ {_: :span,class: :comment, c: l}},' ',
169
+ {_: :a, href: '/@'+u.sub('#','%23')+'?filter=frag',
170
+ c: '&gt;&gt;'}]}}]},'</table>'])}
171
+
172
+ end
data/infod/W/search.rb ADDED
@@ -0,0 +1,33 @@
1
+ class E
2
+
3
+ fn '/search/GET',->e,r{
4
+ r.q['graph'] = 'roonga'
5
+ e.response
6
+ }
7
+
8
+ fn 'head/search',->d,e{[{_: :title, c: e.q['q']},(Fn 'head.formats',e)]}
9
+
10
+ fn 'view/search',->d,e{
11
+ [H.css('/css/search'),H.js('/js/search'),
12
+ (Fn 'view/search/form',e.q,e),'<br><br>',
13
+ (Fn 'view/page',d,e)]}
14
+
15
+ fn 'view/search/form',-> q=nil,e { q||={}
16
+ {:class => :form,
17
+ c: {_: :form, action: e['REQUEST_PATH'],
18
+ c: [{_: :input, name: :q, value: q['q']}, # search box
19
+ q.update(q['view'] ? {} : {'view' => 'search'}). # show searchbox above results unless other view specified
20
+ except('q','start','uri'). # new query & offset for this search
21
+ map{|a,s|
22
+ {_: :input, name: a, value: s, :type => :hidden}}]}}}
23
+
24
+ # construct p/o index-traversal links
25
+ fn 'view/linkPO',->d,e{
26
+ ['<style>a {background-color: #000;text-decoration:none;border-style:dotted;border-width:.1em;border-color:#fff;;color:#fff;font-size:1.3em;border-radius:.62em;padding:.1em}
27
+ div {display:block; padding:.3em}</style>',
28
+ {_: :h3, c: e['uri']},{_: :br},
29
+ d.map{|u,r|
30
+ {c: {_: :a, href: r.url+'?set=indexPO&p='+e['uri']+'&view=page&views=timegraph,mail&v=multi&c=8', c: u}}
31
+ }]}
32
+
33
+ end
data/infod/W/shell.rb ADDED
@@ -0,0 +1,30 @@
1
+ class E
2
+
3
+ class << self
4
+ def console; ARGV.clear; require 'irb'
5
+ IRB.start
6
+ end
7
+ end
8
+
9
+ # util, prefix -> tripleStream
10
+ def triplrStdOut e,f='/',g=nil,a=sh
11
+
12
+ # leading/trailing whitespace expression
13
+ g ||= /^\s*(.*?)\s*$/
14
+
15
+ # exec command
16
+ `#{e} #{a}|grep :`.each_line{|i|
17
+
18
+ # key/val separator
19
+ i = i.split /:/
20
+
21
+ yield uri, # subject
22
+ (f+(i[0].match(g)||[nil,i[0]])[1].gsub(/\s/,'_').gsub(/\//,'-').gsub(/[\(\)]+/,'')), # predicate
23
+ i.tail.join(':').strip.do{|v|v.match(/^[0-9\.]+$/) ? v.to_f : v} # object
24
+ }
25
+ nil
26
+ rescue
27
+ nil
28
+ end
29
+
30
+ end