infod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/infod +37 -0
- data/config.ru +3 -0
- data/infod/Es/fs.rb +154 -0
- data/infod/Es/groonga.rb +101 -0
- data/infod/Es/redis.rb +3 -0
- data/infod/Es/sqlite.rb +3 -0
- data/infod/Es.rb +67 -0
- data/infod/H.rb +29 -0
- data/infod/K.rb +197 -0
- data/infod/N.rb +248 -0
- data/infod/Rb.rb +71 -0
- data/infod/Th/404.rb +55 -0
- data/infod/Th/500.rb +10 -0
- data/infod/Th/GET.rb +132 -0
- data/infod/Th/HEAD.rb +5 -0
- data/infod/Th/PATCH.rb +5 -0
- data/infod/Th/POST.rb +19 -0
- data/infod/Th/local.rb +22 -0
- data/infod/Th/uid.rb +24 -0
- data/infod/Th.rb +110 -0
- data/infod/W/audio.rb +56 -0
- data/infod/W/blog.rb +3 -0
- data/infod/W/cal.rb +110 -0
- data/infod/W/chat.rb +81 -0
- data/infod/W/color.rb +28 -0
- data/infod/W/core.rb +77 -0
- data/infod/W/css.rb +24 -0
- data/infod/W/csv.rb +13 -0
- data/infod/W/du.rb +35 -0
- data/infod/W/edit.rb +8 -0
- data/infod/W/examine/examine.rb +59 -0
- data/infod/W/examine/exhibit.rb +34 -0
- data/infod/W/examine/hist.rb +55 -0
- data/infod/W/examine/history.rb +19 -0
- data/infod/W/examine/normal.rb +31 -0
- data/infod/W/examine/protovis.rb +30 -0
- data/infod/W/examine/sw.rb +114 -0
- data/infod/W/examine/time/graph.rb +86 -0
- data/infod/W/examine/time/line.rb +24 -0
- data/infod/W/feed.rb +116 -0
- data/infod/W/find.rb +24 -0
- data/infod/W/forum.rb +3 -0
- data/infod/W/grep.rb +27 -0
- data/infod/W/html.rb +143 -0
- data/infod/W/image.rb +61 -0
- data/infod/W/json.rb +44 -0
- data/infod/W/kv.rb +66 -0
- data/infod/W/ls.rb +50 -0
- data/infod/W/mail.rb +248 -0
- data/infod/W/page.rb +30 -0
- data/infod/W/pdf.rb +16 -0
- data/infod/W/post.rb +9 -0
- data/infod/W/rdf.rb +32 -0
- data/infod/W/schema.rb +172 -0
- data/infod/W/search.rb +33 -0
- data/infod/W/shell.rb +30 -0
- data/infod/W/source.rb +35 -0
- data/infod/W/table.rb +87 -0
- data/infod/W/text.rb +94 -0
- data/infod/W/tree.rb +26 -0
- data/infod/W/vfs.rb +175 -0
- data/infod/W/wiki.rb +18 -0
- data/infod/W.rb +34 -0
- data/infod/Y.rb +17 -0
- data/infod/infod.rb +13 -0
- data/infod.rb +13 -0
- 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: '↓'},
|
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: '↓',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: '↑'} 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*(>)(>|\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*(>|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: '←'}},
|
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: '→'}}]
|
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
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: '>>'}]}}]},'</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
|