infod 0.0.1

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