infod 0.0.3.3 → 0.0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/infod.rb +2 -1
- data/infod/404.rb +6 -7
- data/infod/500.rb +4 -40
- data/infod/GET.rb +15 -16
- data/infod/HTTP.rb +5 -1
- data/infod/{mime.rb → MIME.rb} +0 -2
- data/infod/POST.rb +29 -28
- data/infod/SPARQL.rb +6 -0
- data/infod/WebID.rb +3 -0
- data/infod/blog.rb +35 -13
- data/infod/constants.rb +1 -3
- data/infod/edit.rb +10 -8
- data/infod/facets.rb +2 -2
- data/infod/feed.rb +149 -108
- data/infod/fs.rb +33 -6
- data/infod/graph.rb +50 -46
- data/infod/groonga.rb +8 -9
- data/infod/html.rb +34 -67
- data/infod/image.rb +2 -2
- data/infod/infod.rb +2 -1
- data/infod/lambda.rb +2 -6
- data/infod/mail.rb +29 -24
- data/infod/man.rb +1 -1
- data/infod/microblog.rb +10 -1
- data/infod/names.rb +9 -20
- data/infod/rdf.rb +5 -4
- data/infod/schema.rb +4 -16
- data/infod/text.rb +1 -1
- data/infod/time.rb +1 -15
- metadata +35 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 394fa91160ccafe206a32071d20edbaafbe1eef8
|
4
|
+
data.tar.gz: 5011996cc3fff07d31d2988b10226243082e668c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb8da61e738b9efd9e8df67e0c904ccd0a9db0247b38594d2720b5740368da1702687a2d6589f3a3b6b767dd00c2f34794bdb741223ce82e2e78f934899d2388
|
7
|
+
data.tar.gz: cdc489d2660c7dfe8f44f9048dd0b1ba88267ba6484f3cd4d31daf58c4bbe85bdc7efaed742878f9bc893fa7b7bb2f745391d2668a2a85754d0a1d9d4e91d8d0
|
data/infod.rb
CHANGED
data/infod/404.rb
CHANGED
@@ -23,15 +23,12 @@ s[HTTP+'statusCodeValue'] = [404]
|
|
23
23
|
|
24
24
|
[404,{'Content-Type'=> r.format},[e.render(r.format,g,r)]]}
|
25
25
|
|
26
|
-
F['/cache/GET'] = F[E404]
|
27
|
-
|
28
26
|
fn 'view/404',->d,e{
|
29
27
|
[H.css('/css/404'),{_: :style, c: "a {background-color:#{R.cs}}"},
|
30
28
|
d.html]}
|
31
29
|
|
32
|
-
#
|
33
|
-
|
34
|
-
m[d.uri] = {}
|
30
|
+
fn 'protograph/blank',->d,_,m{ # 404 is determined by #empty?
|
31
|
+
m[d.uri] = {} # insert a resource
|
35
32
|
rand.to_s.h}
|
36
33
|
|
37
34
|
def checkURIs
|
@@ -40,9 +37,11 @@ s[HTTP+'statusCodeValue'] = [404]
|
|
40
37
|
puts c.join ' '
|
41
38
|
c } # status, uri tuple
|
42
39
|
puts "\n\n"
|
43
|
-
r.map{|c|
|
44
|
-
# show anomalies
|
40
|
+
r.map{|c| # inspect anomalies
|
45
41
|
puts c.join(' ') unless c[0] == 200 }
|
46
42
|
end
|
47
43
|
|
44
|
+
F['/cache/GET'] = F[E404]
|
45
|
+
F['/index/GET'] = F[E404]
|
46
|
+
|
48
47
|
end
|
data/infod/500.rb
CHANGED
@@ -1,54 +1,18 @@
|
|
1
|
-
#watch __FILE__
|
2
1
|
class R
|
3
|
-
|
4
|
-
Errors ||= {}
|
5
|
-
|
6
|
-
fn 'E500',->x,e{
|
7
|
-
stack = x.backtrace
|
8
|
-
$stderr.puts [500, e['REQUEST_URI'], x.class, x.message, stack[0]].join ' '
|
9
|
-
|
10
|
-
Errors[e['uri']] ||= {}
|
11
|
-
Errors[e['uri']][:time] = Time.now
|
12
|
-
Errors[e['uri']][:env] = [e, x.class.to_s, x.message, stack.join('<br>')]
|
13
2
|
|
3
|
+
fn 'E500',->x,e{ $stderr.puts [500, e['REQUEST_URI'], x.class, x.message].join ' '
|
14
4
|
[500,{'Content-Type'=>'text/html'},
|
15
5
|
[H[{_: :html,
|
16
|
-
c: [{_: :head,
|
17
|
-
c: [{_: :title, c: 500},(H.css '/css/500')]},
|
6
|
+
c: [{_: :head,c: [{_: :title, c: 500},(H.css '/css/500')]},
|
18
7
|
{_: :body,
|
19
8
|
c: [{_: :h1, c: 500},
|
20
9
|
{_: :table,
|
21
10
|
c: [{_: :tr,c: [{_: :td, c: {_: :b, c: x.class}},{_: :td, class: :space},{_: :td, class: :message, c: x.message.hrefs}]},
|
22
|
-
|
11
|
+
x.backtrace.map{|f| p = f.split /:/, 3
|
23
12
|
{_: :tr,
|
24
13
|
c: [{_: :td, class: :path, c: p[0].abbrURI},
|
25
14
|
{_: :td, class: :index, c: p[1]},
|
26
15
|
{_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
|
27
16
|
|
28
|
-
F['/500/GET'] = ->e,r{
|
29
|
-
body = H [Errors.sort_by{|u,r|r[:time]}.reverse.html, H.css('/css/500')]
|
30
|
-
[200, {'Content-Type'=>'text/html; charset=utf-8'}, [body]]}
|
31
|
-
|
32
|
-
F['view/'+COGS+'Exception']=->e,r{
|
33
|
-
e.values.map{|e|
|
34
|
-
{style: 'border-radius:1em;background-color:#333;color:#eee;float:left;max-width:42em',
|
35
|
-
c: [{_: :h2, c: "<b style='background-color:#f00'>¿</b>"+(e[Title]||[]).to_s},
|
36
|
-
e[Content]||''
|
37
|
-
]}}}
|
38
|
-
|
39
|
-
# filesystem takes priority over this if it's found
|
40
|
-
fn '/css/500.css/GET',->e,r{
|
41
|
-
[200,{'Content-Type'=>'text/css'},["
|
42
|
-
body {margin:0; font-family: sans-serif; background-color:#fff; color:#000}
|
43
|
-
h1 {padding:.2em; background-color:#f00; color:#fff; margin:0}
|
44
|
-
div {display:inline}
|
45
|
-
table {border-spacing:0;margin:0}
|
46
|
-
b {background-color:#eee;color:#500;padding:.1em .3em .1em .3em}
|
47
|
-
.frag {font-weight:bold; color:#000; background-color:#{R.cs}}
|
48
|
-
td.space {background-color:#ddd}
|
49
|
-
td.message {background-color:#009;color:#fff}
|
50
|
-
td.path {text-align:right}
|
51
|
-
td.index {text-align:right;border-color:#000;border-width:0 0 .1em 0;border-style:dotted;background-color:#ddd;color:#000}
|
52
|
-
td.context {border-color:#ddd;border-width:0 0 .1em 0;border-style:dotted;padding:.15em}"]]}
|
53
|
-
|
54
17
|
end
|
18
|
+
|
data/infod/GET.rb
CHANGED
@@ -14,7 +14,7 @@ class R
|
|
14
14
|
a = @r.accept.values.flatten
|
15
15
|
accepted = a.empty? || (a.member? file.mimeP) || (a.member? '*/*')
|
16
16
|
(!accepted || MIMEcook[file.mimeP] || @r.q.has_key?('view')) ?
|
17
|
-
resource : (file.env @r).
|
17
|
+
resource : (file.env @r).fileGET
|
18
18
|
|
19
19
|
else
|
20
20
|
resource
|
@@ -29,13 +29,13 @@ class R
|
|
29
29
|
[200,{},[]]
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
32
|
+
def fileGET
|
33
33
|
@r['ETag'] = [m,size].h
|
34
34
|
maybeSend mimeP,->{self}
|
35
35
|
end
|
36
36
|
|
37
37
|
def resource
|
38
|
-
# bubble up
|
38
|
+
# bubble up full-hostname then path tree until handled
|
39
39
|
pathSegment.do{|path|
|
40
40
|
lambdas = path.cascade.map{|p| p.uri.t + 'GET' }
|
41
41
|
['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
|
@@ -48,31 +48,30 @@ class R
|
|
48
48
|
|
49
49
|
def response
|
50
50
|
|
51
|
-
m = {}
|
51
|
+
m = {} # TODO request-model as RDF::Graph, benchmark vs our pickle-able JSON graphs, + write persistable RDF::Repository class of similar performance
|
52
52
|
|
53
|
-
#
|
54
|
-
g = @r.q['graph']
|
53
|
+
# Model
|
54
|
+
g = @r.q['graph'] # model identity
|
55
55
|
graphID = (g && F['protograph/' + g] || F['protograph/'])[self,@r.q,m]
|
56
56
|
|
57
|
-
return F[E404][self,@r] if m.empty?
|
57
|
+
return F[E404][self,@r] if m.empty? # protograph empty - nothing found
|
58
58
|
|
59
|
-
#
|
59
|
+
# View
|
60
60
|
@r['ETag'] ||= [@r.q['view'].do{|v|F['view/' + v] && v}, graphID, @r.format, Watch].h
|
61
61
|
|
62
62
|
maybeSend @r.format, ->{
|
63
|
-
|
64
63
|
r = R '/cache/view/' + @r['ETag'].dive
|
65
|
-
if r.e # exists?
|
64
|
+
if r.e # view exists?
|
66
65
|
r
|
67
66
|
else
|
68
67
|
c = R '/cache/model/' + graphID.dive
|
69
|
-
if c.e # exists?
|
68
|
+
if c.e # model exists?
|
70
69
|
m = c.r true
|
71
70
|
else
|
72
|
-
(g && F['graph/' + g] || F['graph/'])[self, @r.q,m]
|
73
|
-
c.w m,true # cache
|
71
|
+
(g && F['graph/' + g] || F['graph/'])[self, @r.q,m]
|
72
|
+
c.w m,true # model -> cache
|
74
73
|
end
|
75
|
-
r.w render @r.format, m, @r #
|
74
|
+
r.w render @r.format, m, @r # view -> cache
|
76
75
|
end }
|
77
76
|
end
|
78
77
|
|
@@ -98,13 +97,13 @@ class R
|
|
98
97
|
[304,{},[]] # client has response
|
99
98
|
end
|
100
99
|
|
101
|
-
# user-patchable
|
100
|
+
# user-patchable root-level handler - index.html or continue to default-handler
|
102
101
|
fn '/GET',->e,r{
|
103
102
|
x = 'index.html'
|
104
103
|
i = [e,e.pathSegment].compact.map{|e|e.as x}.find &:e
|
105
104
|
if i && !r['REQUEST_URI'].match(/\?/)
|
106
105
|
if e.uri[-1] == '/' # inside dir?
|
107
|
-
i.env(r).
|
106
|
+
i.env(r).fileGET # show index
|
108
107
|
else # descend into dir
|
109
108
|
[301, {Location: e.uri.t}, []]
|
110
109
|
end
|
data/infod/HTTP.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
#watch __FILE__
|
2
2
|
class R
|
3
3
|
|
4
|
+
def env r=nil
|
5
|
+
r ? (@r = r; self) : @r
|
6
|
+
end
|
7
|
+
|
4
8
|
def R.call e
|
5
9
|
e.extend Th # HTTP utility functions
|
6
|
-
dev
|
10
|
+
dev # watched files changed?
|
7
11
|
e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
|
8
12
|
path = CGI.unescape e['REQUEST_PATH'].force_encoding('UTF-8').gsub '+','%2B'
|
9
13
|
resource = R['http://'+e['SERVER_NAME']+path]
|
data/infod/{mime.rb → MIME.rb}
RENAMED
@@ -137,9 +137,7 @@ class R
|
|
137
137
|
|
138
138
|
# prefer a view even if requested file exists
|
139
139
|
MIMEcook={
|
140
|
-
'application/atom+xml' => true,
|
141
140
|
'application/markdown' => true,
|
142
|
-
'application/json+rdf' => true,
|
143
141
|
'application/org' => true,
|
144
142
|
'application/postscript' => true,
|
145
143
|
'application/textile' => true,
|
data/infod/POST.rb
CHANGED
@@ -2,42 +2,43 @@
|
|
2
2
|
class R
|
3
3
|
|
4
4
|
def POST
|
5
|
-
#
|
6
|
-
pathSegment.do{|path|
|
5
|
+
pathSegment.do{|path| # handler cascade
|
7
6
|
lambdas = path.cascade.map{|p| p.uri.t + 'POST' }
|
8
7
|
['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
|
9
|
-
F[h + p].do{|fn|
|
10
|
-
|
11
|
-
return r
|
12
|
-
}}}}}
|
13
|
-
basicPOST
|
14
|
-
end
|
15
|
-
def basicPOST
|
16
|
-
return [303,{'Location'=>uri},[]]
|
8
|
+
F[h + p].do{|fn|fn[self,@r].do{|r| return r }}}}}
|
9
|
+
|
17
10
|
case @r['CONTENT_TYPE']
|
18
11
|
when /^application\/x-www-form-urlencoded/
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
formPOST
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def formPOST
|
17
|
+
changed = false
|
18
|
+
params = (Rack::Request.new @r).params
|
19
|
+
params.map{|k,v|
|
20
|
+
s, p, tripleX = JSON.parse CGI.unescape k rescue JSON::ParserError
|
21
|
+
if s # parse successful?
|
22
|
+
s = s.R # subject URI
|
23
|
+
pp = s.predicatePath p # s+p path
|
24
|
+
o = v.match(/\A(\/|http)[\S]+\Z/) ? v.R : F['cleanHTML'][v] # HTML cleanup
|
25
|
+
tripleY = pp.objectPath(o)[0] # editable triple
|
26
|
+
if tripleX.to_s != tripleY.to_s # changed?
|
27
|
+
tripleX && tripleX.R.do{|t| t.delete if t.e } # remove triple
|
28
|
+
s[p] = o unless o.class==String && o.empty? # add triple
|
28
29
|
changed = true
|
29
|
-
end}
|
30
|
-
if changed
|
31
|
-
g = {}
|
32
|
-
fromStream g, :triplrDoc
|
33
|
-
if g.empty? # no triples left
|
34
|
-
ef.delete
|
35
|
-
else # snapshot to graph-doc
|
36
|
-
ef.w g, true
|
37
30
|
end
|
31
|
+
end}
|
32
|
+
if changed # update doc
|
33
|
+
g = {} # triples -> graph
|
34
|
+
fromStream g, :triplrDoc
|
35
|
+
if g.empty? # 0 triples
|
36
|
+
ef.delete
|
37
|
+
else # graph -> doc #TODO write doc-per-version and symlink current (optional history)
|
38
|
+
ef.w g, true
|
38
39
|
end
|
39
40
|
end
|
40
|
-
[303,{'Location'=>uri+'?graph=edit'},[]]
|
41
|
+
[303,{'Location'=>uri+'?graph=edit'+(params['mono'] ? '&mono' : '')},[]]
|
41
42
|
end
|
42
43
|
|
43
44
|
end
|
data/infod/SPARQL.rb
ADDED
data/infod/WebID.rb
ADDED
data/infod/blog.rb
CHANGED
@@ -1,20 +1,29 @@
|
|
1
1
|
#watch __FILE__
|
2
2
|
class R
|
3
3
|
|
4
|
+
# traverse collection of blog-posts
|
5
|
+
F['/blog/GET'] = -> d,e {
|
6
|
+
e.q['set'] = 'depth' # post-range in date-order
|
7
|
+
e.q['local'] = true # hostname-specific paths
|
8
|
+
e.q['c'] = 8 # count
|
9
|
+
R['http://'+e['SERVER_NAME']+'/time'].env(e).response}
|
10
|
+
|
11
|
+
# decode POSTed title, mint derived-URI, set title+type properties, continue to editor
|
4
12
|
F['/blog/post/POST'] = -> d,e {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
post
|
9
|
-
post[Title] =
|
10
|
-
|
11
|
-
[303,{'Location' =>
|
13
|
+
host = 'http://' + e['SERVER_NAME']
|
14
|
+
title = (Rack::Request.new d.env).params['title']
|
15
|
+
base = R[host+Time.now.strftime('/%Y/%m/%d/')+URI.escape(title.gsub /[?#\s\/]/,'_')] # doc
|
16
|
+
post = base.a '#' # editable resource
|
17
|
+
post[Type] = R[SIOCt+'BlogPost']; post[Title] = title # add type-tag & title
|
18
|
+
base.ef.ln_s R[host + '/time/' + Time.now.iso8601[0..18].gsub('-','/') + '.e'] # add to datetime-index
|
19
|
+
[303,{'Location' => (base+"?prototype=sioct:BlogPost&graph=edit&mono").uri},[]]}
|
12
20
|
|
21
|
+
# POST post-title to /blog/post
|
13
22
|
F['/blog/post/GET'] = -> d,e {
|
14
23
|
[200,{'Content-Type'=>'text/html'},
|
15
|
-
[H(['
|
24
|
+
[H(['title',
|
16
25
|
{_: :form, method: :POST,
|
17
|
-
c: [{_: :input, name: :
|
26
|
+
c: [{_: :input, name: :title, style: "font-size:1.6em;width:48ex"},
|
18
27
|
{_: :input, type: :submit, value: ' go '}
|
19
28
|
]}])]]}
|
20
29
|
|
@@ -25,11 +34,11 @@ class R
|
|
25
34
|
case u # match against URIs for customized view
|
26
35
|
when /artery.wbur/ # compact whitespace a bit
|
27
36
|
r[Content] = {class: :WBUR, c: [{_: :style, c: ".WBUR p {margin:0}"},r[Content]]}
|
28
|
-
F['view/base'][{u => r},e
|
37
|
+
F['view/base'][{u => r},e]
|
29
38
|
|
30
39
|
when /boston\.com/ # crop sharebuttons
|
31
40
|
(Nokogiri::HTML.parse r[Content][0]).css('p')[0].do{|p|r[Content]=p.inner_html}
|
32
|
-
F['view/base'][{u => r},e
|
41
|
+
F['view/base'][{u => r},e]
|
33
42
|
|
34
43
|
when /flickr/
|
35
44
|
r[Content]
|
@@ -44,8 +53,21 @@ class R
|
|
44
53
|
c: [{_: :img, src: '/logos/uhub.png',style: 'position:absolute;top:-93px'},
|
45
54
|
{_: :h2, style: 'color:#000;margin:0',c: r[Title]}]},c.to_s],
|
46
55
|
style: 'float:left;max-width:40em;position:relative;background-color:#fff;border-color:#eee;margin-top:93px;margin-right:.3em;padding-top:0;border-style:dotted;border-width:.3em;border-radius:0 .8em .8em .8em'}
|
47
|
-
else
|
48
|
-
|
56
|
+
else # host matches current
|
57
|
+
if u.index('http://'+e['SERVER_NAME']) == 0
|
58
|
+
[{_: :a, href: u, c: {_: :h1, c: r[Title]}},
|
59
|
+
{_: :p, c: r[Content]},
|
60
|
+
# F['view/comment'][{u => r},e]
|
61
|
+
]
|
62
|
+
else # non-local (imported) host
|
63
|
+
F['view/base'][{u => r},e]
|
64
|
+
end
|
49
65
|
end}}
|
50
66
|
|
67
|
+
F['view/comment'] = -> g,e {
|
68
|
+
g.map{|u,r|
|
69
|
+
"comment"
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
51
73
|
end
|
data/infod/constants.rb
CHANGED
data/infod/edit.rb
CHANGED
@@ -28,10 +28,9 @@ class R
|
|
28
28
|
puts "graph.edit #{e}"
|
29
29
|
e.fromStream g, :triplrDoc} # add fs-sourced triples
|
30
30
|
|
31
|
-
=begin HTML <form>
|
32
|
-
|
33
|
-
|
34
|
-
predicate - initialize field for a particular predicate
|
31
|
+
=begin HTML <form> triple-editor
|
32
|
+
@prototype bundle of fields for a type
|
33
|
+
@predicate add field for a particular predicate
|
35
34
|
=end
|
36
35
|
fn 'view/edit',->g,e{
|
37
36
|
|
@@ -54,6 +53,7 @@ class R
|
|
54
53
|
e.q['prototype'].do{|pr| pr = pr.expand
|
55
54
|
Prototypes[pr].do{|v|ps.concat v }} # prototype imports
|
56
55
|
e.q['predicate'].do{|p|ps.push p } # explicit predicate
|
56
|
+
mono = e.q.has_key? 'mono' # 1:1 predicate->object
|
57
57
|
|
58
58
|
[H.css('/css/html'),
|
59
59
|
{_: :form, name: :editor, method: :POST, action: e['REQUEST_PATH'],
|
@@ -68,10 +68,12 @@ class R
|
|
68
68
|
{_: :tr,
|
69
69
|
c: [{_: :td, class: :key, c: {_: :a, title: p, href: p, c: p.abbrURI}}, # property
|
70
70
|
{_: :td,
|
71
|
-
c: [r[p].do{|o|
|
72
|
-
|
73
|
-
triple[s,p,o]}},
|
74
|
-
triple[s,p,nil]]
|
71
|
+
c: [r[p].do{|o| # object?
|
72
|
+
o.justArray.map{|o| # each object
|
73
|
+
triple[s,p,o]}}, # extant triples
|
74
|
+
(triple[s,p,nil] unless r[p] && mono) # add triple
|
75
|
+
]}]}}]}},
|
76
|
+
({_: :input, type: :hidden, name: :mono, value: :true} if mono),
|
75
77
|
{_: :input, type: :submit, value: 'save'}]}]}
|
76
78
|
|
77
79
|
# select a property to edit
|
data/infod/facets.rb
CHANGED
@@ -17,7 +17,7 @@ class R
|
|
17
17
|
# facet stats
|
18
18
|
m.map{|s,r| a.map{|p,_|
|
19
19
|
r[p].do{|o|
|
20
|
-
|
20
|
+
o.justArray.map{|o|
|
21
21
|
a[p][o]=(a[p][o]||0)+1}}}}
|
22
22
|
|
23
23
|
|
@@ -31,7 +31,7 @@ class R
|
|
31
31
|
m.map{|u,r| # each resource
|
32
32
|
a.map{|p,_| # each facet
|
33
33
|
[n[p], r[p].do{|o| # value
|
34
|
-
|
34
|
+
o.justArray.map{|o|
|
35
35
|
n[o.to_s] # identifier
|
36
36
|
}}].join ' '
|
37
37
|
}.do{|f|
|