infod 0.0.3.3 → 0.0.3.4
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.
- 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|
|